diff options
| author | MobileMachine\jeremy <[email protected]> | 2017-06-06 22:59:03 -0400 |
|---|---|---|
| committer | MobileMachine\jeremy <[email protected]> | 2017-06-06 22:59:03 -0400 |
| commit | 24725fa8681f906ab44d80687c09fecc171a2896 (patch) | |
| tree | 312a601df29aca7f8db9f44082d96ebc7a679138 /Core/Scripts/Interfaces | |
| parent | Initial commit (diff) | |
| download | artv2-24725fa8681f906ab44d80687c09fecc171a2896.tar.xz artv2-24725fa8681f906ab44d80687c09fecc171a2896.zip | |
Initial Submission
First submission of current state of ARTv2. Currently considered to be in Alpha. There are a couple of animation tools not implemented yet, and one module not implemented yet, as well as incomplete documentation.
Diffstat (limited to 'Core/Scripts/Interfaces')
42 files changed, 24377 insertions, 0 deletions
diff --git a/Core/Scripts/Interfaces/ART_AddModuleToCanvas.py b/Core/Scripts/Interfaces/ART_AddModuleToCanvas.py new file mode 100644 index 0000000..a339b5b --- /dev/null +++ b/Core/Scripts/Interfaces/ART_AddModuleToCanvas.py @@ -0,0 +1,327 @@ +""" +Author: Jeremy Ernst +""" + +# import statements +import maya.cmds as cmds + +import System.utils as utils +import System.interfaceUtils as interfaceUtils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_AddModuleToCanvas(object): + """ + This tool presents a UI that lists all modules not currently on the animation picker canvas, and allows the user + to choose modules from the list to add to the animation picker canvas. This is an animator-facing tool. + + .. image:: /images/addModuleToCanvas.png + + """ + + def __init__(self, animPickerUI, modulesToAdd, parent=None): + """ + initialize the class, get the QSettings for the tool, and call on buildUI. + + :param animPickerUI: the instance of the animation picker class that launched this class. + :param modulesToAdd: A list of the modules to add, which is a list of modules currently not on the animPickerUI + + """ + super(ART_AddModuleToCanvas, self).__init__() + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.pickerUI = animPickerUI + self.modules = [] + self.modulesToAdd = modulesToAdd + + # assign close event + self.closeEvent = self.closeWin + + # build the UI + self.buildUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + """ + Builds the interface for the tool, showing a list of modules not currently displayed on the picker. + + """ + + if cmds.window("pyART_AddToCanvasWIN", exists=True): + cmds.deleteUI("pyART_AddToCanvasWIN", wnd=True) + + # create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + # create the mainLayout + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.mainWin.setStyleSheet(self.style) + + self.mainWin.setMinimumSize(QtCore.QSize(250, 400)) + self.mainWin.setMaximumSize(QtCore.QSize(250, 400)) + self.mainWin.resize(250, 400) + + # set qt object name + self.mainWin.setObjectName("pyART_AddToCanvasWIN") + self.mainWin.setWindowTitle("Add Module To Canvas") + + # label, listWidget, button + label = QtWidgets.QLabel("Available Modules:") + label.setProperty("boldFont", True) + self.layout.addWidget(label) + + self.moduleList = QtWidgets.QListWidget() + self.moduleList.setMaximumSize(230, 300) + self.moduleList.setMinimumSize(230, 300) + self.layout.addWidget(self.moduleList) + + # add modules to listWidget + self.addModulesToList() + + # create add button + button = QtWidgets.QPushButton("Add Selected To Canvas") + self.layout.addWidget(button) + button.setObjectName("blueButton") + button.clicked.connect(self.addSelectedToCanvas) + + # show ui + self.mainWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addModulesToList(self): + """ + Take the modules passed into the class, and if the modules do not already exist on the canvas, add them to + the list widget. + + """ + + existing = self.getExistingModules() + + for module in self.modulesToAdd: + if module not in existing: + modName = cmds.getAttr(module + ".moduleName") + + # add to listWIdget + index = self.pickerUI.characterTabs.currentIndex() + widget = self.pickerUI.characterTabs.widget(index) + characterNode = widget.property("charNode") + + item = QtWidgets.QListWidgetItem(modName) + item.setData(QtCore.Qt.UserRole, [module, characterNode]) + self.moduleList.addItem(item) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addSelectedToCanvas(self): + """ + Takes the selected module in the list widget and adds it to the picker canvas. The picker UI is contained in + the self.pickerUI. + + """ + + selected = self.moduleList.currentItem() + module = selected.data(QtCore.Qt.UserRole)[0] + + index = self.pickerUI.characterTabs.currentIndex() + widget = self.pickerUI.characterTabs.widget(index) + characterNode = widget.property("charNode") + + # get inst + modType = cmds.getAttr(module + ".moduleType") + modName = cmds.getAttr(module + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(mod) + + # get the class name from that module file (returns RigModules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + # find the instance of that module + moduleInst = moduleClass(self, modName) + self.modules.append(moduleInst) + + scene = self.getCurrentCanvasTab() + + # find out if charNode has a namespace + if cmds.objExists(characterNode + ".namespace"): + namespace = cmds.getAttr(characterNode + ".namespace") + ":" + else: + namespace = "" + + # pass in the network node and the namespace + picker = moduleInst.pickerUI(scene.sceneRect().center(), self.pickerUI, module, namespace) + scene.addItem(picker[0]) + self.pickerUI.selectionScriptJobs.append(picker[2]) + + # ======================================================================= + # #mirror the module's pickerBorderItem if needed + # ======================================================================= + if picker[1] == True: + picker[0].setTransformOriginPoint(picker[0].boundingRect().center()) + picker[0].setTransform(QtGui.QTransform(-1.0, 0.0, 0.0, 1.0, picker[0].boundingRect().width() * 2, 0.0)) + + children = picker[0].childItems() + if children is not None: + self.mirrorChildren(children) + + row = self.moduleList.row(selected) + self.moduleList.takeItem(row) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def mirrorChildren(self, children): + """ + Take the passed in children and mirror the item so the text is not backwards. When a module is added to the + canvas, if it was a right side module, it will be mirrored. This then unmirrors the text. + + :param children: List of QGraphicsSimpleTextItems that need to be mirrored. + + """ + + # for mirroring text on any child items of a pickerBorderItem + for child in children: + if type(child) == QtWidgets.QGraphicsSimpleTextItem: + child.setTransformOriginPoint(child.boundingRect().center()) + child.setTransform(QtGui.QTransform(-1.0, 0.0, 0.0, 1.0, child.boundingRect().width(), 0.0)) + + children = child.childItems() + if children is not None: + self.mirrorChildren(children) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def getCurrentCanvasTab(self): + """ + Get the current tab of the current character of the animation picker. + :return: returns the QGraphicsScene that items can then be added to. + + """ + # get the current tab index and the widget + index = self.pickerUI.characterTabs.currentIndex() + widget = self.pickerUI.characterTabs.widget(index) + + children = widget.children() + for child in children: + if type(child) == QtWidgets.QTabWidget: + tab = child + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + + canvasChildren = canvasWidget.children() + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + return scene + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getExistingModules(self): + """ + Find all existing modules on the canvas and return those modules as a list. + :return: List of modules whose picker exists on the canvas. + + """ + + # get the current tab index and the widget + index = self.pickerUI.characterTabs.currentIndex() + widget = self.pickerUI.characterTabs.widget(index) + characterNode = widget.property("charNode") + characterNodeModules = cmds.listConnections(characterNode + ".rigModules") + + namespace = None + if cmds.objExists(characterNode + ".namespace"): + namespace = cmds.getAttr(characterNode + ".namespace") + ":" + + returnData = [] + + # get the children of the current tab widget + children = widget.children() + for child in children: + + # if we find a tab widget, search for the gfxScene + if type(child) == QtWidgets.QTabWidget: + tab = child + selectedTab = tab.currentIndex() + + for i in range(tab.count()): + tab.setCurrentIndex(i) + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + canvasChildren = canvasWidget.children() + + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + # get all items in the gfxScene + itemsInScene = scene.items() + + for item in itemsInScene: + # if we find our top level picker item (the borderItem), get it's data + if type(item) == interfaceUtils.pickerBorderItem or item.type() == 3: + module = item.data(QtCore.Qt.UserRole) + + if namespace is None: + if module not in returnData: + returnData.append(module) + else: + if (namespace + module) not in returnData: + returnData.append(namespace + module) + + tab.setCurrentIndex(selectedTab) + + return returnData + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeWin(self, event): + """ + deletes the UI. + + """ + + cmds.deleteUI("pyART_AddToCanvasWIN", wnd=True) diff --git a/Core/Scripts/Interfaces/ART_AddModuleUI.py b/Core/Scripts/Interfaces/ART_AddModuleUI.py new file mode 100644 index 0000000..498d1b9 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_AddModuleUI.py @@ -0,0 +1,352 @@ +""" +Author: Jeremy Ernst +""" + +import maya.cmds as cmds + +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + +windowTitle = "Add Module" +windowObject = "pyArtAddModuleUi" + + +class ART_AddModule_UI(QtWidgets.QMainWindow): + """ + This class builds a UI used by the rig creator, and is called when a user pushed a module button to add to their + rig. This UI presents options like prefix, suffix, and ability to specify the parent module bone. + + .. image:: /images/addModuleUI.png + + """ + + def __init__(self, baseName, className, rigUiInst, parent=None): + """ + Initialize the class, taking in the base name of the module to be added, the name of the class of the module + to be added, and the instance of the rig creator UI. Then build the interface for the tool. + + :param baseName: The base name of the module to be added, defined in the module class file at the top. + :param className: The class name of the module to be added, so we can then initialize that module. + :param rigUiInst: The instance of the rig creator UI, from which this function was called. + + """ + + super(ART_AddModule_UI, self).__init__(parent) + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + # create class variables + self.baseName = baseName + self.className = className + self.rigUiInst = rigUiInst + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + style = f.read() + f.close() + + self.setStyleSheet(style) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + # set qt object name + self.setObjectName(windowObject) + self.setWindowTitle(windowTitle) + + # create the mainLayout for the rig creator UI + self.mainLayout = QtWidgets.QHBoxLayout(self.mainWidget) + self.mainLayout.setContentsMargins(0, 0, 0, 0) + + self.setSizePolicy(mainSizePolicy) + self.setMinimumSize(QtCore.QSize(500, 220)) + self.setMaximumSize(QtCore.QSize(500, 520)) + + # create the background + self.frame = QtWidgets.QFrame() + self.frame.setObjectName("mid") + self.mainLayout.addWidget(self.frame) + + # create the layout for the widgets + self.column1layout = QtWidgets.QVBoxLayout(self.frame) + self.mainLayout.addLayout(self.column1layout) + + self.column2Layout = QtWidgets.QVBoxLayout() + self.mainLayout.addLayout(self.column2Layout) + + font = QtGui.QFont() + font.setBold(True) + + label = QtWidgets.QLabel("Choose Parent Bone") + label.setFont(font) + label.setAlignment(QtCore.Qt.AlignCenter) + self.column2Layout.addWidget(label) + + self.boneSearch = QtWidgets.QLineEdit() + self.column2Layout.addWidget(self.boneSearch) + self.boneSearch.setPlaceholderText("Search...") + self.boneSearch.textChanged.connect(self.searchList) + + self.hierarchyTree = QtWidgets.QListWidget() + self.column2Layout.addWidget(self.hierarchyTree) + + # add items to listWidget + parents = utils.getViableParents() + for bone in parents: + self.hierarchyTree.addItem(bone) + + if bone == "root": + index = parents.index(bone) + self.hierarchyTree.setCurrentRow(index) + + # create the prefix pair of fields + self.prefixForm = QtWidgets.QFormLayout() + self.column1layout.addLayout(self.prefixForm) + + self.prefixLabel = QtWidgets.QLabel("Prefix: ") + self.prefixForm.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.prefixLabel) + + self.prefix = QtWidgets.QLineEdit() + self.prefixForm.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.prefix) + + # hookup signal/slot connection + self.prefix.textChanged.connect(self.updatePreview) + + # create the suffix pair of fields + self.suffixForm = QtWidgets.QFormLayout() + self.column1layout.addLayout(self.suffixForm) + + self.suffixLabel = QtWidgets.QLabel("Suffix: ") + self.suffixForm.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.suffixLabel) + + self.suffix = QtWidgets.QLineEdit() + self.suffixForm.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.suffix) + + # hookup signal/slot connection + self.suffix.textChanged.connect(self.updatePreview) + + self.previewLabel = QtWidgets.QLabel("Module Name: ") + self.column1layout.addWidget(self.previewLabel) + + self.previewName = QtWidgets.QLabel(self.baseName) + self.previewName.setMinimumSize(QtCore.QSize(255, 25)) + self.previewName.setMaximumSize(QtCore.QSize(255, 25)) + self.previewName.setAlignment(QtCore.Qt.AlignHCenter) + self.column1layout.addWidget(self.previewName) + + # set preview font + font = QtGui.QFont() + font.setPointSize(12) + self.previewName.setFont(font) + + spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.column1layout.addItem(spacerItem1) + + # special cases (arms and legs) + specialCaseModules = ["ART_Leg_Standard", "ART_Arm_Standard"] + if className in specialCaseModules: + # spacer + groupBox = QtWidgets.QGroupBox("") + self.column1layout.addWidget(groupBox) + layout = QtWidgets.QVBoxLayout(groupBox) + + self.radioButtonLayout = QtWidgets.QHBoxLayout() + layout.addLayout(self.radioButtonLayout) + self.rightRadioBtn = QtWidgets.QRadioButton("Right Side") + self.leftRadioBtn = QtWidgets.QRadioButton("Left Side") + self.radioButtonLayout.addWidget(self.rightRadioBtn) + self.radioButtonLayout.addWidget(self.leftRadioBtn) + self.leftRadioBtn.setChecked(True) + + # spacer + spacerItem2 = QtWidgets.QSpacerItem(20, 80, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.column1layout.addItem(spacerItem2) + + # create button + self.createButton = QtWidgets.QPushButton("CREATE") + self.column1layout.addWidget(self.createButton) + self.createButton.setMinimumSize(QtCore.QSize(255, 40)) + self.createButton.setMaximumSize(QtCore.QSize(255, 40)) + self.createButton.setSizePolicy(mainSizePolicy) + self.createButton.setObjectName("blueButton") + font = QtGui.QFont() + font.setPointSize(12) + self.createButton.setFont(font) + + # hookup signal/slot on create button + self.createButton.clicked.connect(self.createModule) + + self.hierarchyTree.setFocus() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def updatePreview(self): + """ + Read the prefix and suffix QLineEdits, append an underscore, and update the previewName QLabel with the + prefix, basename, and suffix to show what the final module name will be. + + """ + + prefix = str(self.prefix.text()) + suffix = str(self.suffix.text()) + + string = "" + if len(prefix) > 0: + string += prefix + "_" + + string += self.baseName + + if len(suffix) > 0: + string += "_" + suffix + + self.previewName.setText(string) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createModule(self): + """ + Instantiate our module class to create with the user specified name, creating the network node, building + the Skeleton Settings UI for the module, adding the joint mover for that module (importing the joint mover + file), and adding the joint mover to the outliner. + + """ + + mod = __import__("RigModules." + self.className, {}, {}, [self.className]) + reload(mod) + + # get the class name from that module file (returns RigModules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + jmPath = mod.jointMover + + # find the instance of that module and call on the skeletonSettings_UI function + userSpecName = str(self.previewName.text()) + + # check to see if a module already has that name + modules = utils.returnRigModules() + validName = False + for module in modules: + name = cmds.getAttr(module + ".moduleName") + if name == userSpecName: + cmds.confirmDialog(title="Name Exists", + message="A module with that name already exists. Please enter a unique name.", + icon="critical") + return + + # call functions to create network node, skeleton settings UI + moduleInst = moduleClass(self.rigUiInst, userSpecName) + self.rigUiInst.moduleInstances.append(moduleInst) # add this instance to the ui's list of module instances + networkNode = moduleInst.buildNetwork() + + # figure out side + specialCaseModules = ["ART_Leg_Standard", "ART_Arm_Standard"] + if self.className in specialCaseModules: + side = "Left" + if self.rightRadioBtn.isChecked(): + side = "Right" + cmds.setAttr(networkNode + ".side", lock=False) + cmds.setAttr(networkNode + ".side", "Right", type="string", lock=True) + + # build new jmPath name + jmPath = jmPath.partition(".ma")[0] + "_" + side + ".ma" + + moduleInst.skeletonSettings_UI(userSpecName) + moduleInst.jointMover_Build(jmPath) + moduleInst.addJointMoverToOutliner() + + # update the created joints attribute on the network node with the new names + prefix = str(self.prefix.text()) + suffix = str(self.suffix.text()) + + if len(prefix) > 0: + prefix = prefix + "_" + if len(suffix) > 0: + suffix = "_" + suffix + + createdBones = cmds.getAttr(networkNode + ".Created_Bones") + createdBones = createdBones.split("::") + + attrString = "" + for bone in createdBones: + if len(bone) > 1: + attrString += prefix + bone + suffix + "::" + + cmds.setAttr(networkNode + ".Created_Bones", lock=False) + cmds.setAttr(networkNode + ".Created_Bones", attrString, type="string", lock=True) + + # update the self.currentParent label and the parentModuleBone attr on the network node + parent = (self.hierarchyTree.currentItem().text()) + moduleInst.currentParent.setText(parent) + + cmds.setAttr(networkNode + ".parentModuleBone", lock=False) + cmds.setAttr(networkNode + ".parentModuleBone", parent, type="string", lock=True) + + # parent the joint mover to the offset mover of the parent + mover = "" + + if parent == "root": + mover = "root_mover" + + else: + # find the parent mover name to parent to + networkNodes = utils.returnRigModules() + mover = utils.findMoverNodeFromJointName(networkNodes, parent, False, True) + + if mover is not None: + cmds.parentConstraint(mover, userSpecName + "_mover_grp", mo=True) + cmds.scaleConstraint(mover, userSpecName + "_mover_grp") + + # create the connection geo between the two + globalMover = utils.findGlobalMoverFromName(userSpecName) + cmds.select(globalMover) + cmds.setToolTo("moveSuperContext") + + utils.fitViewAndShade() + + # delete the UI + cmds.deleteUI(windowObject) + + # obey the current UI visibility toggles + self.rigUiInst.setMoverVisibility() + moduleInst.updateBoneCount() + self.rigUiInst.populateNetworkList() + + # turn on aim mode + moduleInst.aimMode(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def searchList(self): + """ + Get the search text from the QLineEdit and search the items in the QListWidget for any matches. Anything that + does not match will be hidden in the QListWidget. + + """ + + searchText = self.boneSearch.text() + + for i in range(self.hierarchyTree.count()): + lwItem = self.hierarchyTree.item(i) + if lwItem.text().find(searchText) != -1: + lwItem.setHidden(False) + else: + lwItem.setHidden(True) diff --git a/Core/Scripts/Interfaces/ART_AddOrRemoveInfluences.py b/Core/Scripts/Interfaces/ART_AddOrRemoveInfluences.py new file mode 100644 index 0000000..288f996 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_AddOrRemoveInfluences.py @@ -0,0 +1,361 @@ +""" +Author: Jeremy Ernst +""" + +from functools import partial + +import maya.cmds as cmds +import maya.mel as mel + +import System.riggingUtils as riggingUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_AddOrRemoveInfluences(): + """ + This class is used to list influences in or out of the current skinCluster, and then remove or add said + influences given current selection. + + It is called from this button, found after finalizing your setup: + .. image:: /images/addRemoveInfsButton.png + + This is what the full interface looks like: + .. image:: /images/addRemoveInfs.png + + """ + + def __init__(self, mainUI): + """ + Instantiates the class, taking in the instance of the rig creator skin tools interface. Get settings values + from QSettings. Build the interface. + + :param mainUI: Instance of the skin tools interface. + + """ + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + # build the UI + self.buildInterface() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildInterface(self): + """ + Builds the interface for the tool, finding all joints that compose the asset, comparing them to joints in the + skinCluster, then separating the initial list into joints in the cluster, and joints not in the cluster. + + """ + + if cmds.window("ART_addRemoveInfsWin", exists=True): + cmds.deleteUI("ART_addRemoveInfsWin", wnd=True) + + # launch a UI to get the name information + self.addRemoveInfsWin = QtWidgets.QMainWindow(self.mainUI) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.addRemoveInfsWin_mainWidget = QtWidgets.QWidget() + self.addRemoveInfsWin.setCentralWidget(self.addRemoveInfsWin_mainWidget) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + # set qt object name + self.addRemoveInfsWin.setObjectName("ART_addRemoveInfsWin") + self.addRemoveInfsWin.setWindowTitle("Add/Remove Influences") + + # create the mainLayout for the ui + self.addRemoveInfsWin_mainLayout = QtWidgets.QVBoxLayout(self.addRemoveInfsWin_mainWidget) + self.addRemoveInfsWin_mainLayout.setContentsMargins(5, 5, 5, 5) + + self.addRemoveInfsWin.resize(300, 450) + self.addRemoveInfsWin.setSizePolicy(mainSizePolicy) + self.addRemoveInfsWin.setMinimumSize(QtCore.QSize(300, 450)) + self.addRemoveInfsWin.setMaximumSize(QtCore.QSize(300, 450)) + + # create the background image + self.addRemoveInfsWin_frame = QtWidgets.QFrame() + self.addRemoveInfsWin_mainLayout.addWidget(self.addRemoveInfsWin_frame) + self.addRemoveInfsWin_frame.setObjectName("dark") + + # create the main layout for the widgets + self.addRemoveInfsWin_widgetLayout = QtWidgets.QHBoxLayout(self.addRemoveInfsWin_frame) + + # two layouts needed for the widget layout. left side = vertical layout for filters, + # search, and list. right layout = vertical layout for buttons + self.addRemoveInfsWin_leftSideLayout = QtWidgets.QVBoxLayout() + self.addRemoveInfsWin_widgetLayout.addLayout(self.addRemoveInfsWin_leftSideLayout) + + self.addRemoveInfsWin_rightSideLayout = QtWidgets.QVBoxLayout() + self.addRemoveInfsWin_widgetLayout.addLayout(self.addRemoveInfsWin_rightSideLayout) + + # left side: filters, search, list + self.addRemoveInfsWin_filters = QtWidgets.QComboBox() + self.addRemoveInfsWin_leftSideLayout.addWidget(self.addRemoveInfsWin_filters) + self.addRemoveInfsWin_filters.addItem("Show Influences In Skin") + self.addRemoveInfsWin_filters.addItem("Show Influences Not In Skin") + self.addRemoveInfsWin_filters.currentIndexChanged.connect(partial(self.addOrRemoveInfs_ShowInfsFilter)) + + self.addRemoveInfsWin_search = QtWidgets.QLineEdit() + self.addRemoveInfsWin_leftSideLayout.addWidget(self.addRemoveInfsWin_search) + self.addRemoveInfsWin_search.setPlaceholderText("Search...") + self.addRemoveInfsWin_search.textChanged.connect(partial(self.addOrRemoveInfs_Search)) + + self.addRemoveInfsWin_infList = QtWidgets.QListWidget() + self.addRemoveInfsWin_leftSideLayout.addWidget(self.addRemoveInfsWin_infList) + self.addRemoveInfsWin_infList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + + # right side: add button, remove button, prune weights, remove unused button + font = QtGui.QFont() + font.setPointSize(8) + font.setBold(True) + + self.addRemoveInfsWin_refreshSelBtn = QtWidgets.QPushButton("Refresh") + self.addRemoveInfsWin_rightSideLayout.addWidget(self.addRemoveInfsWin_refreshSelBtn) + self.addRemoveInfsWin_refreshSelBtn.setMinimumSize(110, 35) + self.addRemoveInfsWin_refreshSelBtn.setMaximumSize(110, 35) + self.addRemoveInfsWin_refreshSelBtn.setFont(font) + self.addRemoveInfsWin_refreshSelBtn.clicked.connect(partial(self.addOrRemoveInfs_RefreshSelection)) + self.addRemoveInfsWin_refreshSelBtn.setObjectName("blueButton") + self.addRemoveInfsWin_rightSideLayout.addSpacerItem( + QtWidgets.QSpacerItem(100, 300, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)) + + self.addRemoveInfsWin_addInfBtn = QtWidgets.QPushButton("Add") + self.addRemoveInfsWin_rightSideLayout.addWidget(self.addRemoveInfsWin_addInfBtn) + self.addRemoveInfsWin_addInfBtn.setMinimumSize(110, 35) + self.addRemoveInfsWin_addInfBtn.setMaximumSize(110, 35) + self.addRemoveInfsWin_addInfBtn.setFont(font) + self.addRemoveInfsWin_addInfBtn.clicked.connect(partial(self.addOrRemoveInfs_addInf, True, False)) + self.addRemoveInfsWin_addInfBtn.setObjectName("blueButton") + + self.addRemoveInfsWin_removeInfBtn = QtWidgets.QPushButton("Remove") + self.addRemoveInfsWin_rightSideLayout.addWidget(self.addRemoveInfsWin_removeInfBtn) + self.addRemoveInfsWin_removeInfBtn.setMinimumSize(110, 35) + self.addRemoveInfsWin_removeInfBtn.setMaximumSize(110, 35) + self.addRemoveInfsWin_removeInfBtn.setFont(font) + self.addRemoveInfsWin_removeInfBtn.clicked.connect(partial(self.addOrRemoveInfs_addInf, False, False)) + self.addRemoveInfsWin_removeInfBtn.setObjectName("blueButton") + + self.addRemoveInfsWin_removeUnusedInfBtn = QtWidgets.QPushButton("Remove Unused") + self.addRemoveInfsWin_rightSideLayout.addWidget(self.addRemoveInfsWin_removeUnusedInfBtn) + self.addRemoveInfsWin_removeUnusedInfBtn.setMinimumSize(110, 35) + self.addRemoveInfsWin_removeUnusedInfBtn.setMaximumSize(110, 35) + self.addRemoveInfsWin_removeUnusedInfBtn.setFont(font) + self.addRemoveInfsWin_removeUnusedInfBtn.clicked.connect(partial(self.addOrRemoveInfs_addInf, False, True)) + self.addRemoveInfsWin_removeUnusedInfBtn.setObjectName("blueButton") + + self.addRemoveInfsWin_pruneBtn = QtWidgets.QPushButton("Prune Weights") + self.addRemoveInfsWin_rightSideLayout.addWidget(self.addRemoveInfsWin_pruneBtn) + self.addRemoveInfsWin_pruneBtn.setMinimumSize(110, 35) + self.addRemoveInfsWin_pruneBtn.setMaximumSize(110, 35) + self.addRemoveInfsWin_pruneBtn.setFont(font) + self.addRemoveInfsWin_pruneBtn.clicked.connect(partial(self.addOrRemoveInfs_prune)) + self.addRemoveInfsWin_pruneBtn.setObjectName("blueButton") + + # populate infList + self.addOrRemoveInfs_RefreshSelection() + + # show window + self.addRemoveInfsWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addOrRemoveInfs_RefreshSelection(self): + """ + Regenerate the lists comparing all joints of the rig to joints in the skin cluster and joints not in the + skinCluster. Clear the listWidgets and refresh with new data. + + """ + + self.addRemoveInfsWin_infList.clear() + + # get selection, find skin cluster, find influences in skinCluster, and populate listWidget + selection = cmds.ls(sl=True) + if len(selection) > 0: + skinCluster = riggingUtils.findRelatedSkinCluster(selection[0]) + + if skinCluster is not None: + skinInfs = cmds.skinCluster(skinCluster, q=True, inf=True) + + for inf in skinInfs: + self.addRemoveInfsWin_infList.addItem(inf) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def addOrRemoveInfs_ShowInfsFilter(self): + """ + Change what is displayed in the QListWidget based on the QComboBox setting of which joints to show: those in + the skinCluster, or those not in the skinCluster. + + """ + + self.addRemoveInfsWin_infList.clear() + currentIndex = self.addRemoveInfsWin_filters.currentIndex() + + selection = cmds.ls(sl=True) + if len(selection) > 0: + skinCluster = riggingUtils.findRelatedSkinCluster(selection[0]) + + if skinCluster is not None: + skinInfs = cmds.skinCluster(skinCluster, q=True, inf=True) + # if filter set to show infs in cluster: + if currentIndex == 0: + for each in skinInfs: + self.addRemoveInfsWin_infList.addItem(each) + + # if filter set to show non-skinned infs + if currentIndex == 1: + # get full path + dagPath = cmds.ls(skinInfs[0], long=True)[0] + rootJoint = dagPath.partition("|")[2].partition("|")[0] + currentSelection = cmds.ls(sl=True)[0] + + # get all joints + cmds.select(rootJoint, hi=True) + skeleton = cmds.ls(sl=True) + cmds.select(currentSelection) + + # compare all joints to infs list + for each in skeleton: + if each not in skinInfs: + self.addRemoveInfsWin_infList.addItem(each) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def addOrRemoveInfs_Search(self): + """ + Hides all items in the QListWidget, then compares the QLineEdit search text with each item in the QListWidget, + and if the search text is found in the text of an item, show that QListWidgetItem. + + """ + + searchKey = self.addRemoveInfsWin_search.text() + + # get all items in the joint list + allItems = [] + for i in range(self.addRemoveInfsWin_infList.count()): + item = self.addRemoveInfsWin_infList.item(i) + itemName = item.text() + allItems.append([item, itemName]) + + # hide all items in list + for item in allItems: + item[0].setHidden(True) + item[0].setSelected(False) + + # find items in list with search key and show item + if searchKey.find("*") == 0: + matchedItems = self.addRemoveInfsWin_infList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchWildcard) + for item in matchedItems: + item.setHidden(False) + item.setSelected(True) + + else: + matchedItems = self.addRemoveInfsWin_infList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchContains) + for item in matchedItems: + item.setHidden(False) + item.setSelected(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def addOrRemoveInfs_addInf(self, add, removeUnused): + """ + Either add, or remove, selected influences from the QListWidget to/from the skinCluster. (the main function) + + :param add: Whether or not to add or remove. + :param removeUnused: If this flag is set, remove any unweighted influences in the skinCluster. + + """ + + # get current selection in scene + currentSelection = cmds.ls(sl=True) + + # get selected items in infList + selectedItems = self.addRemoveInfsWin_infList.selectedItems() + + # find the skinCluster + selection = cmds.ls(sl=True) + if len(selection) > 0: + skinCluster = riggingUtils.findRelatedSkinCluster(selection[0]) + + # removed unused influences + if removeUnused: + weightedInfs = cmds.skinCluster(skinCluster, q=True, weightedInfluence=True) + allInfs = cmds.skinCluster(skinCluster, q=True, inf=True) + + for inf in allInfs: + if inf not in weightedInfs: + cmds.skinCluster(skinCluster, edit=True, ri=inf) + + cmds.select(currentSelection) + self.addOrRemoveInfs_RefreshSelection() + self.addOrRemoveInfs_ShowInfsFilter() + return + + # add selectedItems to the skinCluster + for item in selectedItems: + if add: + cmds.skinCluster(skinCluster, edit=True, ai=item.text(), wt=0, lw=True) + if not add: + cmds.skinCluster(skinCluster, edit=True, ri=item.text()) + + # refresh list + cmds.select(currentSelection) + self.addOrRemoveInfs_RefreshSelection() + self.addOrRemoveInfs_ShowInfsFilter() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addOrRemoveInfs_prune(self): + """ + Calls on Maya's built in prune skin weights tool. + + """ + + mel.eval("PruneSmallWeightsOptions;") + + # get current selection in scene + currentSelection = cmds.ls(sl=True) + + # refresh lists + cmds.select(currentSelection) + self.addOrRemoveInfs_RefreshSelection() + self.addOrRemoveInfs_ShowInfsFilter() diff --git a/Core/Scripts/Interfaces/ART_AimModeUI.py b/Core/Scripts/Interfaces/ART_AimModeUI.py new file mode 100644 index 0000000..90492da --- /dev/null +++ b/Core/Scripts/Interfaces/ART_AimModeUI.py @@ -0,0 +1,230 @@ +""" +Author: Jeremy Ernst +""" + +from functools import partial + +import maya.cmds as cmds + +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_AimMode(): + """ + This class creates a tool that allows a rigger to toggle aim mode for a given selection of modules. Aim mode + ensures that the parent joints in a joint mover are always aiming at their children. + + This tool can be called from the toolbar on the Rig Creator UI from the following button: + .. image:: /images/aimModeButton.png + + The full interface looks like this: + .. image:: /images/aimMode.png + + """ + + def __init__(self, mainUI): + """ + Instantiates the class, getting the QSettings for the tool, and building the Aim Mode interface. + + :param mainUI: The instance of the Rig Creator UI this tool was called from. + + """ + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + # build the UI + self.buildAimModeUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildAimModeUI(self): + """ + Builds the Aim Mode interface, finding all modules that have the ability to aim, and listing those modules + as well as their current aim status. + + """ + + if cmds.window("ART_AimModeWin", exists=True): + cmds.deleteUI("ART_AimModeWin", wnd=True) + + # launch a UI to get the name information + self.aimModeWin = QtWidgets.QMainWindow(self.mainUI) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.aimModeWin.setStyleSheet(self.style) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.aimModeWin_mainWidget = QtWidgets.QWidget() + self.aimModeWin.setCentralWidget(self.aimModeWin_mainWidget) + + # set qt object name + self.aimModeWin.setObjectName("ART_AimModeWin") + self.aimModeWin.setWindowTitle("Aim Mode") + + # create the mainLayout for the rig creator UI + self.aimModeWin_mainLayout = QtWidgets.QVBoxLayout(self.aimModeWin_mainWidget) + self.aimModeWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.aimModeWin.resize(400, 250) + self.aimModeWin.setSizePolicy(mainSizePolicy) + self.aimModeWin.setMinimumSize(QtCore.QSize(400, 250)) + self.aimModeWin.setMaximumSize(QtCore.QSize(400, 250)) + + # create the background image + self.aimModeWin_frame = QtWidgets.QFrame() + self.aimModeWin_mainLayout.addWidget(self.aimModeWin_frame) + + # create the layout for the widgets + self.aimModeWin_widgetLayout = QtWidgets.QHBoxLayout(self.aimModeWin_frame) + self.aimModeWin_widgetLayout.setContentsMargins(5, 5, 5, 5) + + # add the QListWidget Frame + self.aimModeWin_moduleListFrame = QtWidgets.QFrame() + self.aimModeWin_moduleListFrame.setMinimumSize(QtCore.QSize(275, 200)) + self.aimModeWin_moduleListFrame.setMaximumSize(QtCore.QSize(275, 200)) + self.aimModeWin_moduleListFrame.setContentsMargins(20, 0, 20, 0) + + # create the list widget + self.aimModeWin_moduleList = QtWidgets.QListWidget(self.aimModeWin_moduleListFrame) + self.aimModeWin_widgetLayout.addWidget(self.aimModeWin_moduleListFrame) + self.aimModeWin_moduleList.setMinimumSize(QtCore.QSize(265, 200)) + self.aimModeWin_moduleList.setMaximumSize(QtCore.QSize(265, 200)) + self.aimModeWin_moduleList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.aimModeWin_moduleList.setSpacing(3) + + # add the layout for the buttons + self.aimModeWin_buttonLayoutAll = QtWidgets.QVBoxLayout() + self.aimModeWin_widgetLayout.addLayout(self.aimModeWin_buttonLayoutAll) + self.aimModeWin_buttonLayoutAll.setContentsMargins(5, 20, 5, 20) + + # add the selection buttons + self.aimModeWin_selectionButtonLayout = QtWidgets.QVBoxLayout() + self.aimModeWin_buttonLayoutAll.addLayout(self.aimModeWin_selectionButtonLayout) + self.aimModeWin_selectAllButton = QtWidgets.QPushButton("Select All") + self.aimModeWin_selectAllButton.setMinimumSize(QtCore.QSize(115, 25)) + self.aimModeWin_selectAllButton.setMaximumSize(QtCore.QSize(115, 25)) + self.aimModeWin_selectionButtonLayout.addWidget(self.aimModeWin_selectAllButton) + self.aimModeWin_selectAllButton.clicked.connect(self.aimModeWin_moduleList.selectAll) + self.aimModeWin_selectAllButton.setObjectName("blueButton") + + self.aimModeWin_selectNoneButton = QtWidgets.QPushButton("Clear Selection") + self.aimModeWin_selectNoneButton.setMinimumSize(QtCore.QSize(115, 25)) + self.aimModeWin_selectNoneButton.setMaximumSize(QtCore.QSize(115, 25)) + self.aimModeWin_selectionButtonLayout.addWidget(self.aimModeWin_selectNoneButton) + self.aimModeWin_selectNoneButton.clicked.connect(self.aimModeWin_moduleList.clearSelection) + self.aimModeWin_selectNoneButton.setObjectName("blueButton") + + # spacer + spacerItem = QtWidgets.QSpacerItem(20, 80, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.aimModeWin_selectionButtonLayout.addItem(spacerItem) + + # add the buttons for reset settings and reset transforms + self.aimModeWin_turnOnAim = QtWidgets.QPushButton("On") + self.aimModeWin_turnOnAim.setMinimumSize(QtCore.QSize(115, 25)) + self.aimModeWin_turnOnAim.setMaximumSize(QtCore.QSize(115, 25)) + self.aimModeWin_selectionButtonLayout.addWidget(self.aimModeWin_turnOnAim) + self.aimModeWin_turnOnAim.setToolTip("Turn on Aim Mode for selected modules.") + self.aimModeWin_turnOnAim.clicked.connect(partial(self.aimModeUI_Toggle, True)) + self.aimModeWin_turnOnAim.setObjectName("blueButton") + + self.aimModeWin_turnOffAim = QtWidgets.QPushButton("Off") + self.aimModeWin_turnOffAim.setMinimumSize(QtCore.QSize(115, 25)) + self.aimModeWin_turnOffAim.setMaximumSize(QtCore.QSize(115, 25)) + self.aimModeWin_selectionButtonLayout.addWidget(self.aimModeWin_turnOffAim) + self.aimModeWin_turnOffAim.setToolTip("Turn off Aim Mode for selected modules.") + self.aimModeWin_turnOffAim.clicked.connect(partial(self.aimModeUI_Toggle, False)) + self.aimModeWin_turnOffAim.setObjectName("blueButton") + + # populate the list widget + modules = utils.returnRigModules() + for module in modules: + # get module name + moduleName = cmds.getAttr(module + ".moduleName") + + # figure out if the module supports aimMode + canAim = False + if cmds.objExists(module + ".canAim"): + canAim = cmds.getAttr(module + ".canAim") + + # see if it is currently in aimMode + aimMode = cmds.getAttr(module + ".aimMode") + + # if it does, add it to the listwidget + if canAim: + + # font + headerFont = QtGui.QFont() + headerFont.setPointSize(10) + headerFont.setBold(True) + + # create the listWidgetItem + pixmap = QtGui.QPixmap(10, 10) + pixmap.fill(QtGui.QColor(0, 255, 0)) + icon = QtGui.QIcon(pixmap) + + pixmapOff = QtGui.QPixmap(10, 10) + pixmapOff.fill(QtGui.QColor(255, 0, 0)) + iconOff = QtGui.QIcon(pixmapOff) + + item = QtWidgets.QListWidgetItem(iconOff, moduleName) + item.setFont(headerFont) + item.setTextAlignment(QtCore.Qt.AlignCenter) + item.setData(QtCore.Qt.UserRole, [icon, iconOff]) + + if aimMode: + item.setIcon(icon) + + self.aimModeWin_moduleList.addItem(item) + + # show the window + self.aimModeWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def aimModeUI_Toggle(self, state): + """ + Calls on each selected module's aimMode function and sets the aimMode based on the passed in state. + + :param state: Whether to turn aim mode on or off. + + """ + + selected = self.aimModeWin_moduleList.selectedItems() + items = [] + + for each in selected: + items.append(each.text()) + if state: + each.setIcon(each.data(QtCore.Qt.UserRole)[0]) + if not state: + each.setIcon(each.data(QtCore.Qt.UserRole)[1]) + + # call on each selected module's aimMode function + for each in self.mainUI.moduleInstances: + name = each.name + if name in items: + each.aimMode(state) + + # clear selection + self.aimModeWin_moduleList.clearSelection() diff --git a/Core/Scripts/Interfaces/ART_AnimationUI.py b/Core/Scripts/Interfaces/ART_AnimationUI.py new file mode 100644 index 0000000..da4184b --- /dev/null +++ b/Core/Scripts/Interfaces/ART_AnimationUI.py @@ -0,0 +1,2155 @@ +""" +Author: Jeremy Ernst +""" + +# import statements +import json +import os +from functools import partial + +import maya.cmds as cmds + +import System.interfaceUtils as interfaceUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + +# maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + +_instances = [] + + +def getMainWindow(): + """ + Get Maya's window as a QWidget and return the item in memory. + + :return: a QWidget of Maya's window + + """ + + # get maya's window as QWidget + import maya.OpenMayaUI as mui + pointer = mui.MQtUtil.mainWindow() + # pyside QMainWindow takes in a QWidget rather than QObject + return shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + + +class ART_AnimationUI(QtWidgets.QMainWindow): + """ + This class creates the main animation interface that houses the control picker and the buttons for the animation + tools. This is the main interface that animators will interact with. + + .. image:: /images/animationUI.png + + """ + + def __init__(self, parent=None): + """ + Instantiates the class, getting the QSettings, writing the stylesheets, and calling on the method to build + the interface. + + """ + + _instances.append(self) + + super(ART_AnimationUI, self).__init__(parent) + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.center = QtCore.QPointF(210.000000, 287.500000) + self.scale = 1.0 + self.selectionScriptJobs = [] + + # assign close event + self.closeEvent = self.closeEvent + + # write out qss based on user settings + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir, sheet)) + + # build the UI + self.buildUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + """ + Builds the animation UI that houses the area for control pickers and a sidebar for animation tools. + + """ + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + # create the mainLayout + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.setStyleSheet(self.style) + + self.setMinimumSize(QtCore.QSize(550, 400)) + self.setMaximumSize(QtCore.QSize(550, 750)) + self.resize(550, 750) + + # set qt object name + self.setObjectName("pyART_AnimTools_Win") + self.setWindowTitle("Animation Tools") + + # ======================================================================= + # #create the menu bar + # ======================================================================= + self.menuBar = QtWidgets.QMenuBar() + self.menuBar.setMaximumHeight(20) + self.layout.addWidget(self.menuBar) + + # ======================================================================= + # #create the picker toolbar layout + # ======================================================================= + self.toolFrame = QtWidgets.QFrame() + self.toolFrame.setObjectName("dark") + self.toolFrame.setMinimumHeight(50) + self.toolFrame.setMaximumHeight(50) + + self.layout.addWidget(self.toolFrame) + self.toolbarLayout = QtWidgets.QHBoxLayout(self.toolFrame) + self.toolbarLayout.setDirection(QtWidgets.QBoxLayout.RightToLeft) + self.toolbarLayout.addStretch(0) + self.toolbarLayout.setSpacing(4) + + # ======================================================================= + # #picker toolbar buttons + # ======================================================================= + + self.editPickerBtn = QtWidgets.QPushButton("Edit") + self.editPickerBtn.setMinimumSize(QtCore.QSize(90, 30)) + self.editPickerBtn.setMaximumSize(QtCore.QSize(90, 30)) + self.toolbarLayout.addWidget(self.editPickerBtn) + self.editPickerBtn.setEnabled(False) + self.editPickerBtn.setObjectName("blueButton") + self.editPickerBtn.clicked.connect(self.editPicker) + self.editPickerBtn.setToolTip("Make module pickers editable (move, scale, rotate)") + + self.createPickerBtn = QtWidgets.QPushButton("Create") + self.createPickerBtn.setMinimumSize(QtCore.QSize(90, 30)) + self.createPickerBtn.setMaximumSize(QtCore.QSize(90, 30)) + self.createPickerBtn.setObjectName("blueButton") + self.createPickerBtn.setToolTip("Create a new picker for the given character.") + + self.toolbarLayout.addWidget(self.createPickerBtn) + self.createPickerBtn.clicked.connect(self.createNewPicker) + + self.removeModBtn = QtWidgets.QPushButton() + self.removeModBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.removeModBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/removeModule.png")) + self.removeModBtn.setIconSize(QtCore.QSize(30, 30)) + self.removeModBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.removeModBtn) + self.removeModBtn.setEnabled(False) + self.removeModBtn.setObjectName("toolbar") + self.removeModBtn.clicked.connect(partial(self.removeModuleFromPickerUI)) + self.removeModBtn.setToolTip("Remove a module's picker from the canvas.") + + self.moveModBtn = QtWidgets.QPushButton() + self.moveModBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.moveModBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/movePicker.png")) + self.moveModBtn.setIconSize(QtCore.QSize(30, 30)) + self.moveModBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.moveModBtn) + self.moveModBtn.setEnabled(False) + self.moveModBtn.setObjectName("toolbar") + self.moveModBtn.clicked.connect(partial(self.movePickerToTab)) + self.moveModBtn.setToolTip("Move a picker from one tab to another.") + + self.moduleListBtn = QtWidgets.QPushButton() + self.moduleListBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.moduleListBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/moduleList.png")) + self.moduleListBtn.setIconSize(QtCore.QSize(30, 30)) + self.moduleListBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.moduleListBtn) + self.moduleListBtn.setEnabled(False) + self.moduleListBtn.setObjectName("toolbar") + self.moduleListBtn.clicked.connect(partial(self.addModuleToPickerUI)) + self.moduleListBtn.setToolTip("Add a module's picker to the current canvas.") + + # background image button + self.backgroundImgBtn = QtWidgets.QPushButton() + self.backgroundImgBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.backgroundImgBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/background.png")) + self.backgroundImgBtn.setIconSize(QtCore.QSize(30, 30)) + self.backgroundImgBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.backgroundImgBtn) + self.backgroundImgBtn.setEnabled(False) + self.backgroundImgBtn.setObjectName("toolbar") + self.backgroundImgBtn.setToolTip("Change the background of the current picker tab.") + self.backgroundImgBtn.clicked.connect(partial(self.changeBackground)) + + self.toolbarLayout.addSpacerItem(QtWidgets.QSpacerItem(10, 0)) + + buttonGrp = QtWidgets.QButtonGroup(self.toolbarLayout) + buttonGrp.setExclusive(True) + + self.normalSelectButton = QtWidgets.QPushButton() + self.normalSelectButton.setMinimumSize(QtCore.QSize(30, 30)) + self.normalSelectButton.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/normalSelect_On.png")) + self.normalSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.normalSelectButton.setIcon(icon) + self.toolbarLayout.addWidget(self.normalSelectButton) + self.normalSelectButton.setCheckable(True) + self.normalSelectButton.setObjectName("toolbar") + self.normalSelectButton.setChecked(True) + self.normalSelectButton.setToolTip("Change picker selection mode to normal selection") + self.normalSelectButton.clicked.connect(self.toggleDragState) + + self.dragSelectButton = QtWidgets.QPushButton() + self.dragSelectButton.setMinimumSize(QtCore.QSize(30, 30)) + self.dragSelectButton.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/dragSelect.png")) + self.dragSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.dragSelectButton.setIcon(icon) + self.toolbarLayout.addWidget(self.dragSelectButton) + self.dragSelectButton.setCheckable(True) + self.dragSelectButton.setObjectName("toolbar") + self.dragSelectButton.setToolTip("Change picker selection mode to drag selection") + self.dragSelectButton.clicked.connect(self.toggleDragState) + + self.commentButton = QtWidgets.QPushButton() + self.commentButton.setMinimumSize(QtCore.QSize(30, 30)) + self.commentButton.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/commentMode.png")) + self.commentButton.setIconSize(QtCore.QSize(30, 30)) + self.commentButton.setIcon(icon) + self.toolbarLayout.addWidget(self.commentButton) + self.commentButton.setCheckable(True) + self.commentButton.setObjectName("toolbar") + self.commentButton.setToolTip("Add Comment Box Mode") + self.commentButton.clicked.connect(self.toggleDragState) + + buttonGrp.addButton(self.normalSelectButton) + buttonGrp.addButton(self.dragSelectButton) + buttonGrp.addButton(self.commentButton) + + # self.toolbarLayout.addSpacerItem(QtWidgets.QSpacerItem(10, 0)) + + self.saveTemplateBtn = QtWidgets.QPushButton() + self.saveTemplateBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.saveTemplateBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/save.png")) + self.saveTemplateBtn.setIconSize(QtCore.QSize(30, 30)) + self.saveTemplateBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.saveTemplateBtn) + self.saveTemplateBtn.setObjectName("toolbar") + self.saveTemplateBtn.clicked.connect(self.savePicker) + self.saveTemplateBtn.setToolTip("Save a picker file") + + self.loadTemplateBtn = QtWidgets.QPushButton() + self.loadTemplateBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.loadTemplateBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/load.png")) + self.loadTemplateBtn.setIconSize(QtCore.QSize(30, 30)) + self.loadTemplateBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.loadTemplateBtn) + self.loadTemplateBtn.setObjectName("toolbar") + self.loadTemplateBtn.clicked.connect(self.loadPicker) + self.loadTemplateBtn.setToolTip("Load a picker file") + + # ======================================================================= + # #create the horizontal layout, with the left column giving us the picker canvas area, + # and the right a toolbar for animation tools + # ======================================================================= + self.mainLayout = QtWidgets.QHBoxLayout() + self.layout.addLayout(self.mainLayout) + + self.mainLeftColumnLayout = QtWidgets.QVBoxLayout() + self.mainLayout.addLayout(self.mainLeftColumnLayout) + + self.mainRightColumnLayout = QtWidgets.QVBoxLayout() + self.mainLayout.addLayout(self.mainRightColumnLayout) + + # ======================================================================= + # #left column - picker canvas + # ======================================================================= + + """ + the picker canvas will consist of a tab layout at the top, one per character in scene, + and in each of those tabs, another tab layout to hold the pages of the picker + """ + + self.pickerMainWidget = QtWidgets.QFrame() + self.pickerMainWidget.setObjectName("epic") + self.mainLeftColumnLayout.addWidget(self.pickerMainWidget) + + self.pickerMainWidget.setMinimumSize(470, 700) + self.pickerMainWidget.setMaximumSize(470, 700) + + self.characterTabs = QtWidgets.QTabWidget(self.pickerMainWidget) + self.characterTabs.setObjectName("tabVariant") + + self.characterTabs.setMinimumSize(450, 670) + self.characterTabs.setMaximumSize(450, 670) + self.characterTabs.setIconSize(QtCore.QSize(75, 75)) + + # look for characters in scene to create character tabs + charactersFound = self.findCharacters() + + if not charactersFound: + cmds.warning("No characters found in scene.") + + # ======================================================================= + # #right column - toolbar + # ======================================================================= + + self.animToolsFrame = QtWidgets.QFrame() + self.animToolsFrame.setObjectName("epic2") + self.mainRightColumnLayout.addWidget(self.animToolsFrame) + self.animToolsFrame.setMinimumSize(80, 750) + self.animToolsFrame.setMaximumSize(80, 750) + + self.animToolsLayout = QtWidgets.QVBoxLayout(self.animToolsFrame) + + self.animToolsLayout.addSpacerItem(QtWidgets.QSpacerItem(75, 75)) + + # Select Controls + self.selectCtrlBtn = QtWidgets.QPushButton() + self.selectCtrlBtn.setMinimumSize(QtCore.QSize(65, 65)) + self.selectCtrlBtn.setMaximumSize(QtCore.QSize(65, 65)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/animSelect.png")) + self.selectCtrlBtn.setIconSize(QtCore.QSize(65, 65)) + self.selectCtrlBtn.setIcon(icon) + self.animToolsLayout.addWidget(self.selectCtrlBtn) + self.selectCtrlBtn.setObjectName("toolbar") + self.selectCtrlBtn.clicked.connect(self.selectAllCtrls) + self.selectCtrlBtn.setToolTip("Select All Controls") + + # Export Motion + + self.exportMotionBtn = QtWidgets.QPushButton() + self.exportMotionBtn.setMinimumSize(QtCore.QSize(65, 65)) + self.exportMotionBtn.setMaximumSize(QtCore.QSize(65, 65)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/export.png")) + self.exportMotionBtn.setIconSize(QtCore.QSize(65, 65)) + self.exportMotionBtn.setIcon(icon) + self.animToolsLayout.addWidget(self.exportMotionBtn) + self.exportMotionBtn.setObjectName("toolbar") + self.exportMotionBtn.clicked.connect(self.exportMotion) + self.exportMotionBtn.setToolTip("Export Motion") + + # Import Motion + self.importMotionBtn = QtWidgets.QPushButton() + self.importMotionBtn.setMinimumSize(QtCore.QSize(65, 65)) + self.importMotionBtn.setMaximumSize(QtCore.QSize(65, 65)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/import.png")) + self.importMotionBtn.setIconSize(QtCore.QSize(65, 65)) + self.importMotionBtn.setIcon(icon) + self.animToolsLayout.addWidget(self.importMotionBtn) + self.importMotionBtn.setObjectName("toolbar") + self.importMotionBtn.clicked.connect(self.importMotion) + self.importMotionBtn.setToolTip("Import Motion") + + # Match Over Frame Range + + movie = QtGui.QMovie(utils.returnNicePath(self.iconsPath, "System/matchRange.gif")) + movie.setParent(self) + self.matchRangeBtn = QtWidgets.QPushButton() + self.matchRangeBtn.setMinimumSize(QtCore.QSize(65, 65)) + self.matchRangeBtn.setMaximumSize(QtCore.QSize(65, 65)) + self.matchRangeBtn.setIconSize(QtCore.QSize(65, 65)) + self.animToolsLayout.addWidget(self.matchRangeBtn) + self.matchRangeBtn.setObjectName("toolbar") + self.matchRangeBtn.clicked.connect(self.matchOverRange) + self.matchRangeBtn.setToolTip("Match Over Frame Range") + + movie.frameChanged.connect(partial(self.setButtonIcon, movie)) + movie.start() + + if movie.loopCount() != -1: + movie.finished.connect(movie.start()) + + # Reset Module + self.resetModuleBtn = QtWidgets.QPushButton() + self.resetModuleBtn.setMinimumSize(QtCore.QSize(65, 65)) + self.resetModuleBtn.setMaximumSize(QtCore.QSize(65, 65)) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/animReset.png")) + self.resetModuleBtn.setIconSize(QtCore.QSize(65, 65)) + self.resetModuleBtn.setIcon(icon) + self.animToolsLayout.addWidget(self.resetModuleBtn) + self.resetModuleBtn.setObjectName("toolbar") + self.resetModuleBtn.clicked.connect(self.resetRigCtrls) + self.resetModuleBtn.setToolTip("Reset modules tool") + + self.animToolsLayout.addSpacerItem(QtWidgets.QSpacerItem(75, 600)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def setButtonIcon(self, movie): + """ + Sets the icon of the matchRangeBtn to the next frame in the passed in movie. Note: This was a test function + to see if animated gifs could be used as buttons. + + :param movie: The movie whose frame to change. + + """ + + self.matchRangeBtn.setIcon(QtGui.QIcon(movie.currentPixmap())) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacters(self): + """ + Finds all assets in the scene built with ARTv2, and creates a picker tab for each one in the animation UI. + + """ + + allNodes = cmds.ls(type="network") + characterNodes = [] + for node in allNodes: + attrs = cmds.listAttr(node) + if "rigModules" in attrs: + characterNodes.append(node) + + if len(characterNodes) == 0: + return False + + else: + # go through each node, find the character name, the namespace on the node, and the picker attribute + for node in characterNodes: + try: + namespace = cmds.getAttr(node + ".namespace") + except: + namespace = cmds.getAttr(node + ".name") + + picker = False + + if cmds.objExists(node + ".pickerFile"): + pickerFile = cmds.getAttr(node + ".pickerFile") + pickerPath = utils.returnNicePath(self.toolsPath, "Core/Pickers/") + + picker = True + + # create the tab for the given namespace + characterWidget = QtWidgets.QWidget() + characterWidget.setProperty("charNode", node) + characterWidget.setMinimumSize(442, 720) + characterWidget.setMaximumSize(442, 720) + characterWidget.setProperty("namespace", namespace) + + # add the icon found on the node's icon path attribute to the tab + iconPath = cmds.getAttr(node + ".iconPath") + iconPath = utils.returnNicePath(self.projectPath, iconPath) + icon = QtGui.QIcon(iconPath) + self.characterTabs.addTab(characterWidget, icon, "") + index = self.characterTabs.indexOf(characterWidget) + self.characterTabs.setTabToolTip(index, namespace) + + # if a picker file existed, load it. Otherwise, display an image telling the user to either + # load a file or create a picker + frame = QtWidgets.QFrame(characterWidget) + frame.setMinimumSize(442, 720) + frame.setMaximumSize(442, 720) + frame.setStyleSheet( + "background-image: url(" + utils.returnNicePath(self.iconsPath, "System/noPicker.png") + ");") + + layout = QtWidgets.QVBoxLayout(frame) + layout.setContentsMargins(20, 150, 20, 241) + + # add help gif + movie_screen = QtWidgets.QLabel(frame) + movie_screen.setStyleSheet("background: transparent;") + layout.addWidget(movie_screen) + moviePath = utils.returnNicePath(self.iconsPath, "Help/createPicker.gif") + movie = QtGui.QMovie(moviePath, QtCore.QByteArray()) + movie.setCacheMode(QtGui.QMovie.CacheAll) + movie.setSpeed(100) + movie_screen.setMovie(movie) + + movie.start() + + if picker: + self.characterTabs.setCurrentIndex(index) + self.loadPicker(utils.returnNicePath(pickerPath, pickerFile)) + + return True + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createNewPicker(self, bypass=False): + """ + Create a new control picker for the given character tab. This will remove the help gif and replace it with a + blank canvas that module pickers can be added to (using ART_AddModuleToCanvas). + + :param bypass: Whether or not to bypass the QMessageBox confirming the creation of a new picker. + This is used when loading a picker from file. bypass will be set to True. + + :return: returns the QTabWidget for this character's picker. + + .. seealso:: ART_AddModuleToCanvas + + """ + + # get the current tab index and the widget + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + closeWidget = widget.children() + + # get the tab text + character = self.characterTabs.tabToolTip(index) + + if not bypass: + # display a message confirming new picker creation for given character + msgBox = QtWidgets.QMessageBox() + msgBox.setText("Create a New Picker for " + str(character) + " ?") + msgBox.setIcon(QtWidgets.QMessageBox.Question) + msgBox.addButton("Yes", QtWidgets.QMessageBox.YesRole) + msgBox.addButton("Cancel", QtWidgets.QMessageBox.NoRole) + ret = msgBox.exec_() + + # if the user selected "Yes", delete the old widget + if ret == 0: + for each in closeWidget: + each.setParent(None) + each.close() + self.update() + + if ret == 1: + return + + else: + for each in closeWidget: + each.setParent(None) + each.close() + self.update() + + # now add a new tab widget to our character tabs, with a new tab named "Main" + pickerTabs = QtWidgets.QTabWidget(widget) + pickerTabs.currentChanged.connect(self.pickerTabChange) + + pickerTabs.setMinimumSize(444, 632) + pickerTabs.setMaximumSize(444, 632) + + # add 'add tab' button + addTabBtn = QtWidgets.QPushButton("Add Tab") + addTabBtn.setObjectName("blueButton") + addTabBtn.setToolTip("Add Picker Tab") + addTabBtn.clicked.connect(partial(self.addTab, pickerTabs)) + pickerTabs.setCornerWidget(addTabBtn, QtCore.Qt.TopRightCorner) + + # style sheet + stylesheet = """ + QTabBar::tab + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + width: 100px; + padding-left: -10px; + } + QTabBar::tab:selected + { + background-color: rgb(14,100,143); + border: 2px solid black; + } + QTabBar::tab:hover + { + background: rgb(19,132,183); + } + QTabBar::tab:!selected + { + margin-top: 5px; + } + QTabWidget::pane + { + border-top: 2px solid rgb(19,132,183); + } + """ + + # create the qWidget for this tab of the picker + pickerCanvas = QtWidgets.QWidget() + pickerCanvas.setMinimumSize(442, 600) + pickerCanvas.setMaximumSize(442, 600) + pickerTabs.addTab(pickerCanvas, "Main") + pickerTabs.setStyleSheet(stylesheet) + + # create the layout for this page + pageLayout = QtWidgets.QVBoxLayout(pickerCanvas) + + # create the graphicsView + gfxView = QtWidgets.QGraphicsView() + pageLayout.addWidget(gfxView) + + # create the qgraphicsScene + gfxScene = QtWidgets.QGraphicsScene() + gfxScene.setSceneRect(0, 0, 420, 575) + gfxView.setScene(gfxScene) + gfxView.setDragMode(QtWidgets.QGraphicsView.NoDrag) + + # set background image + pixmap = QtGui.QPixmap(utils.returnNicePath(self.iconsPath, "System/canvas.png")) + gfxItem = gfxScene.addPixmap(pixmap) + gfxItem.setZValue(-20) + + # show the picker tabs + pickerTabs.show() + + # enable module list button + self.backgroundImgBtn.setEnabled(True) + self.moduleListBtn.setEnabled(True) + self.removeModBtn.setEnabled(True) + self.moveModBtn.setEnabled(True) + self.normalSelectButton.setEnabled(False) + self.dragSelectButton.setEnabled(False) + + if not bypass: + self.addModuleToPickerUI() + + # ======================================================= + # #enable edit button + # ======================================================= + self.editPickerBtn.setEnabled(True) + + return pickerTabs + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleDragState(self): + """ + Toggles selection interaction modes within the QGraphicsScene. The three different modes are normal select, + drag select, and comment box mode. This will set the dragMode of the QGraphicsView to the currently selected + state, along with switching icons to show selection status. + + """ + + # get the current tab index and the widget + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + views = [] + + # get the children of the current tab widget + children = widget.children() + for child in children: + + # if we find a tab widget, search for the gfxScene + if type(child) == QtWidgets.QTabWidget: + tab = child + selectedTab = tab.currentIndex() + + for i in range(tab.count()): + tab.setCurrentIndex(i) + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + canvasChildren = canvasWidget.children() + + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + views.append(view) + + tab.setCurrentIndex(selectedTab) + + for view in views: + view.mousePressEvent = partial(self.gfxViewMousePress, view) + view.mouseMoveEvent = partial(self.gfxViewMouseMove, view) + view.mouseReleaseEvent = partial(self.gfxViewMouseRelease, view) + + # normal singular select mode + if self.normalSelectButton.isChecked(): + for view in views: + view.setDragMode(QtWidgets.QGraphicsView.NoDrag) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/normalSelect_On.png")) + self.normalSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.normalSelectButton.setIcon(icon) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/dragSelect.png")) + self.dragSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.dragSelectButton.setIcon(icon) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/commentMode.png")) + self.commentButton.setIconSize(QtCore.QSize(30, 30)) + self.commentButton.setIcon(icon) + + # drag select mode + if self.dragSelectButton.isChecked(): + for view in views: + view.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/normalSelect.png")) + self.normalSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.normalSelectButton.setIcon(icon) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/dragSelect_On.png")) + self.dragSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.dragSelectButton.setIcon(icon) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/commentMode.png")) + self.commentButton.setIconSize(QtCore.QSize(30, 30)) + self.commentButton.setIcon(icon) + + # create UE4-style comment box mode + if self.commentButton.isChecked(): + for view in views: + view.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/normalSelect.png")) + self.normalSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.normalSelectButton.setIcon(icon) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/dragSelect.png")) + self.dragSelectButton.setIconSize(QtCore.QSize(30, 30)) + self.dragSelectButton.setIcon(icon) + + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/commentMode_on.png")) + self.commentButton.setIconSize(QtCore.QSize(30, 30)) + self.commentButton.setIcon(icon) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def gfxViewMousePress(self, view, event): + """ + Override event that captures a mouse press when in the passed in QGraphicsView and displays the QRubberBand + if in drag select or comment box mode. + + :param view: the QGraphicsView to detect mouse press events in. + + """ + + if self.dragSelectButton.isChecked(): + self.origin = event.pos() + self.rubberband = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, view) + self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize())) + self.rubberband.show() + + if self.commentButton.isChecked(): + self.origin = event.pos() + self.rubberband = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, view) + self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize())) + self.rubberband.show() + + QtWidgets.QGraphicsView.mousePressEvent(view, event) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def gfxViewMouseMove(self, view, event): + """ + Override event that captures a mouse move when in the passed in QGraphicsView and changes the + displayed size of the QRubberBand based on the origin position and the current position, drawing a QRect (if + in drag select or comment box mode.). It also finds any items inside of that QRect (picker buttons, etc). + + :param view: the QGraphicsView to detect mouse move events in and check for items in. + + """ + + if self.dragSelectButton.isChecked(): + try: + self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized()) + self.itemsInRect = view.items(QtCore.QRect(self.origin, event.pos()).normalized()) + except Exception, e: + pass + + if self.commentButton.isChecked(): + try: + self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized()) + self.itemsInRect = view.items(QtCore.QRect(self.origin, event.pos()).normalized()) + except Exception, e: + pass + QtWidgets.QGraphicsView.mouseMoveEvent(view, event) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def gfxViewMouseRelease(self, view, event): + """ + Override event that captures a mouse release when in the passed in QGraphicsView and hides the QRubberBand + if it was visible. If in comment box mode, this release event will also create the comment box with the + dimensions and position of the start point of the mouse press, and the QRect from the mouse move. + + :param view: the QGraphicsView to detect mouse release events and to add comment boxes to. + + """ + + if self.dragSelectButton.isChecked(): + self.rubberband.hide() + try: + for item in self.itemsInRect: + try: + if item.classType == "pickerButton": + item.mousePressEventCustom(QtGui.QGraphicsSceneMouseEvent()) + + except: + pass + except: + pass + + if self.commentButton.isChecked(): + self.rubberband.hide() + + geo = self.rubberband.geometry() + scene = view.scene() + box = interfaceUtils.commentBoxItem(geo.x(), geo.y(), geo.width(), geo.height(), scene, view, self) + scene.addItem(box) + box.setZValue(0) + + self.normalSelectButton.setChecked(True) + self.toggleDragState() + + QtWidgets.QGraphicsView.mouseReleaseEvent(view, event) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addTab(self, tabWidget, bypass=False, tabName=None): + """ + Adds a tab to the QTabWidget for a given character to add more module control pickers to. These are sometimes + referred to as "canvases". + + :param tabWidget: The QTabWidget to add a tab to. + :param bypass: If not creating the "Main" tab or loading a picker from file, a tab name must be entered. + :param tabName: If creating the "Main" tab or loading a picker from file, the name given to the tab that + will be created. + + :return: Index of the created tab in the QTabWidget. + """ + + if not bypass: + tabName, ok = QtWidgets.QInputDialog.getText(self, "Tab Name", "Enter Tab Name:") + else: + ok = True + tabName = tabName + if ok: + if tabName != "": + # create the qWidget for this tab of the picker + pickerCanvas = QtWidgets.QWidget() + pickerCanvas.setMinimumSize(442, 600) + pickerCanvas.setMaximumSize(442, 600) + tabWidget.addTab(pickerCanvas, str(tabName)) + index = tabWidget.indexOf(pickerCanvas) + + # create the layout for this page + pageLayout = QtWidgets.QVBoxLayout(pickerCanvas) + + # create the graphicsView + gfxView = QtWidgets.QGraphicsView() + pageLayout.addWidget(gfxView) + + # create the qgraphicsScene + gfxScene = QtWidgets.QGraphicsScene() + gfxScene.setSceneRect(0, 0, 420, 575) + gfxView.setScene(gfxScene) + + # set background image + pixmap = QtGui.QPixmap(utils.returnNicePath(self.iconsPath, "System/canvas.png")) + gfxScene.addPixmap(pixmap) + + return index + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def closeEvent(self, event): + """ + Closes the interface. + + """ + + for inst in _instances: + + if cmds.dockControl("pyART_animToolsDock", q=True, exists=True): + inst.close() + + else: + if cmds.window("pyART_AnimTools_Win", exists=True): + inst.close() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def addModuleToPickerUI(self): + """ + Creates an instance of ART_AddModuleToCanvas to bring up that tool, passing in modules valid to add. + + """ + + modulesToAdd = self.comparePickerToRig(True, False) + import ART_AddModuleToCanvas as am2c + reload(am2c) + am2c.ART_AddModuleToCanvas(self, modulesToAdd) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def removeModuleFromPickerUI(self): + """ + Creates an instance of the ART_RemoveModuleFromCanvas tool, passing in modules valid to remove. + + """ + + modulesToAdd = self.comparePickerToRig(False, True) + import ART_RemoveModuleFromCanvas as arfc + reload(arfc) + arfc.ART_RemoveModuleFromCanvas(self, modulesToAdd) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def movePickerToTab(self): + """ + Creates an instance of the ART_MovePickerToTabUI tool, passing in all valid module pickers that could be moved. + + """ + + modulesToAdd = self.findAllPickerItems() + import ART_MovePickerToTabUI as mp2t + reload(mp2t) + mp2t.ART_MovePickerToTab(self, modulesToAdd) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def pickerTabChange(self): + """ + Called on when a tab is changed on a given character's picker, if the remove modules from canvas UI is open, + it will be closed. + + .. todo:: This function could eventually be changed to simply refresh that UI with the new information. + + """ + + if cmds.window("pyART_AddToCanvasWIN", exists=True): + title = cmds.window("pyART_AddToCanvasWIN", q=True, title=True) + if title == "Remove Module From Canvas": + cmds.deleteUI("pyART_AddToCanvasWIN", wnd=True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def editPicker(self): + """ + Makes all picker items editable again, making them selectable, movable, scalable, and rotatable. + + """ + + data = self.getPickerTabs(False) + comments = self.getComments() + self.backgroundImgBtn.setEnabled(True) + self.moduleListBtn.setEnabled(True) + self.removeModBtn.setEnabled(True) + self.moveModBtn.setEnabled(True) + self.normalSelectButton.setEnabled(False) + self.dragSelectButton.setEnabled(False) + + for each in data: + item = each.get("item") + item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) + item.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True) + item.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, True) + item.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, True) + + for comment in comments: + item = comment.get("item") + item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) + item.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def savePicker(self): + """ + Save the picker data to a .picker file. The main picker data is actually gathered by self.getPickerTabs, + self.getComments, and self.getButtonColors. + + .. seealso:: ART_AnimationUI.getPickerTabs, ART_AnimationUI.getComments, ART_AnimationUI.getButtonColors + + """ + + # get info from current character tab + pickerData = self.getPickerTabs(True) + commentData = self.getComments(True) + buttonData = self.getButtonColors() + + jsonData = {} + jsonData["pickerData"] = pickerData + jsonData["commentData"] = commentData + jsonData["buttonData"] = buttonData + + # character name + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + characterNode = widget.property("charNode") + + if not cmds.objExists(characterNode + ".pickerFile"): + cmds.addAttr(characterNode, ln="pickerFile", dt="string") + + # ask for the file name to give the picker + startingDir = utils.returnNicePath(self.toolsPath, "Core/Pickers") + if not os.path.exists(startingDir): + os.makedirs(startingDir) + + filename = cmds.fileDialog2(fm=0, okc="Save Picker", dir=startingDir, ff="*.picker") + if filename is not None: + # create the picker file + f = open(filename[0], 'w') + + # dump the data with json + json.dump(jsonData, f) + f.close() + + # write picker file location to character node + niceFileName = utils.returnFriendlyPath(filename[0]) + pickerFile = niceFileName.partition(utils.returnNicePath(self.toolsPath, "Core/Pickers/") + "/")[2] + cmds.setAttr(characterNode + ":.pickerFile", pickerFile, type="string") + + # disable module list button + self.backgroundImgBtn.setEnabled(False) + self.moduleListBtn.setEnabled(False) + self.removeModBtn.setEnabled(False) + self.moveModBtn.setEnabled(False) + self.normalSelectButton.setEnabled(True) + self.dragSelectButton.setEnabled(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def comparePickerToRig(self, unused, used): + """ + Compares the modules that are on the picker to all modules that make up the character. + + :param unused: Whether or not we want a list returned of modules that are not on the picker yet. + :param used: Whether or not we want a list returned of modules that are on the picker. + :return: Returns a list of the modules based on either used or unused args. + + """ + + # get info from current character tab + jsonData = self.getPickerTabs(False) + + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + characterNode = widget.property("charNode") + + rigModules = cmds.listConnections(characterNode + ".rigModules") + returnModules = [] + pickerModules = [] + + for data in jsonData: + pickerModules.append(data.get("module")) + + if unused: + for module in rigModules: + if module not in pickerModules: + returnModules.append(module) + + if used: + returnModules = list(set(pickerModules)) + + return returnModules + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findAllPickerItems(self): + """ + Finds all modules on the picker. Used mostly be the load picker function. + :return: Returns a list of picker data that includes for each item, the module the picker is for, the item in + memory, and the nice name. + + .. seealso:: ART_AnimationUI.loadPicker + """ + + # get the current tab index and the widget + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + returnData = [] + + # get the children of the current tab widget + children = widget.children() + for child in children: + + # if we find a tab widget, search for the gfxScene + if type(child) == QtWidgets.QTabWidget: + tab = child + + for i in range(tab.count()): + tab.setCurrentIndex(i) + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + canvasChildren = canvasWidget.children() + + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + # get all items in the gfxScene + itemsInScene = scene.items() + + for item in itemsInScene: + # if we find our top level picker item (the borderItem), get it's data + if type(item) == interfaceUtils.pickerBorderItem or item.type() == 3: + module = item.data(QtCore.Qt.UserRole) + niceName = item.data(2) + returnData.append([module, item, niceName]) + + return returnData + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def loadPicker(self, filename=None): + """ + Loads a .picker file and builds the picker according to the file data. + :param filename: The path of the picker file to gather data from. + + """ + + if filename is None: + startingDir = utils.returnNicePath(self.toolsPath, "Core/Pickers") + if os.path.exists(startingDir): + + filename = cmds.fileDialog2(fm=1, okc="Load Picker", dir=startingDir, ff="*.picker") + if filename is not None: + filename = filename[0] + + borderItems = [] + + if os.path.exists(filename): + # =============================================================== + # #load the data + # =============================================================== + json_file = open(filename) + data = json.load(json_file) + json_file.close() + + # =============================================================== + # #get character node + # =============================================================== + tabIndex = self.characterTabs.currentIndex() + characterWidget = self.characterTabs.widget(tabIndex) + characterNode = characterWidget.property("charNode") + characterNodeModules = cmds.listConnections(characterNode + ".rigModules") + + if cmds.objExists(characterNode + ".namespace"): + namespace = cmds.getAttr(characterNode + ".namespace") + ":" + else: + namespace = cmds.getAttr(characterNode + ".name") + + # create new picker + tabWidget = self.createNewPicker(True) + + # =============================================================== + # #create all tabs + # =============================================================== + existingTabs = [] + subPickers = [] + for i in range(tabWidget.count()): + tabName = tabWidget.tabText(i) + existingTabs.append(tabName) + + # =============================================================== + # #go through data + # =============================================================== + subPickers = [] + pickerData = data["pickerData"] + + for item in pickerData: + module = item.get("module") + moduleNiceName = item.get("module") + tab = item.get("tab") + transforms = item.get("transforms") + subPicker = item.get("subPicker") + mirrored = item.get("mirrored") + path = item.get("path") + + if module is not None: + # if there is a namespace (if the add character for animation tool was used): + if namespace + module in characterNodeModules: + module = namespace + module + else: + namespace = "" + + if subPicker is True: + subPickers.append([module, tab, transforms, mirrored, moduleNiceName]) + + if tab not in existingTabs: + self.addTab(tabWidget, True, tab) + existingTabs.append(tab) + + # =========================================================== + # #add the picker to the correct tab + # =========================================================== + buildPicker = False + tabChildren = tabWidget.children() + + # get a list of the tabs with their names and index + tabInfo = {} + for i in range(len(tabChildren)): + tabName = tabWidget.tabText(i) + tabInfo[tabName] = i + + # now find a matching entry in that list to our tab variable + index = tabInfo.get(tab) + if index is None: + index = 0 + # set out tab widget to that index + tabWidget.setCurrentIndex(index) + if subPicker is False: + buildPicker = True + + # =========================================================== + # #now that the tab is correct, build the picker for the module + # =========================================================== + if buildPicker: + scene = self.getCurrentScene() + + try: + # create picker for the given module + inst = self.getModuleInst(module) + pickerData = inst.pickerUI(self.center, self, module, namespace) + picker = pickerData[0] + scene.addItem(picker) + self.selectionScriptJobs.append(pickerData[2]) + borderItems.append([picker, moduleNiceName, module]) + + # mirror the module's pickerBorderItem if needed + if pickerData[1] == True: + picker.setTransformOriginPoint(picker.boundingRect().center()) + picker.setTransform( + QtGui.QTransform(-1.0, 0.0, 0.0, 1.0, picker.boundingRect().width() * 2, 0.0)) + + children = picker.childItems() + if children is not None: + self.mirrorChildTextItems(children) + + # set transformations on the picker + picker.setTransformOriginPoint(picker.boundingRect().center()) + picker.setScale(transforms[2]) + + # when loading a picker, set data(1) to be the scale factor that was possibly saved out. + picker.setData(1, transforms[2]) + + picker.setTransformOriginPoint(picker.boundingRect().center()) + picker.setRotation(transforms[1]) + + picker.setPos(QtCore.QPointF(transforms[0][0], transforms[0][1])) + + picker.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, False) + picker.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, False) + picker.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, False) + picker.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, False) + + # ======================================================= + # #enable edit button + # ======================================================= + self.editPickerBtn.setEnabled(True) + + # ======================================================= + # #disable canvas, move, and remove buttons + # ======================================================= + self.backgroundImgBtn.setEnabled(False) + self.moduleListBtn.setEnabled(False) + self.removeModBtn.setEnabled(False) + self.moveModBtn.setEnabled(False) + self.normalSelectButton.setEnabled(True) + self.dragSelectButton.setEnabled(True) + + except Exception, e: + cmds.warning(str(e)) + + try: + if path is not None: + + widget = tabWidget.widget(index) + widgetChildren = widget.children() + for child in widgetChildren: + if type(child) == QtWidgets.QGraphicsView: + gfxView = child + + if os.path.exists(utils.returnFriendlyPath(path)): + + # set background image + pixmap = QtGui.QPixmap() + pixmap.load(utils.returnFriendlyPath(path)) + scene = gfxView.scene() + gfxItem = scene.addPixmap(pixmap) + gfxItem.setZValue(-10) + scene.setProperty("customImg", gfxItem) + scene.setProperty("filePath", filename) + else: + cmds.warning("Tried to load custom background image: " + str( + path) + ", but file not found on disk.") + + except Exception, e: + print e + + # =============================================================== + # #Handle Sub-Pickers + # =============================================================== + if len(subPickers) > 0: + for info in subPickers: + + module = info[4] + tab = info[1] + transforms = info[2] + mirrored = info[3] + pickerItems = self.findAllPickerItems() + + # each item in pickerItems has module, item, niceName. use niceName to see if we have a winner + + for each in pickerItems: + pickerModule = each[0] + pickerItem = each[1] + pickerNiceName = each[2] + + if pickerModule == module: + if pickerNiceName is not None: + # now find a matching entry in that list to our tab variable + index = tabInfo.get(tab) + + # set our tab widget to that index + tabWidget.setCurrentIndex(index) + + # find current scene + scene = self.getCurrentScene() + scene.addItem(pickerItem) + + # set transformations on the picker + pickerItem.setTransformOriginPoint(pickerItem.boundingRect().center()) + pickerItem.setScale(transforms[2]) + + # when loading a picker, set data(1) to be the scale factor that was possibly saved out. + pickerItem.setData(1, transforms[2]) + + pickerItem.setTransformOriginPoint(pickerItem.boundingRect().center()) + pickerItem.setRotation(transforms[1]) + + pickerItem.setPos(QtCore.QPointF(transforms[0][0], transforms[0][1])) + + if mirrored == -1: + pickerItem.setTransformOriginPoint(pickerItem.boundingRect().center()) + pickerItem.setTransform( + QtGui.QTransform(-1.0, 0.0, 0.0, 1.0, scene.sceneRect().width(), 0.0)) + pickerItem.setTransformOriginPoint(pickerItem.boundingRect().center()) + + # lock down pickers + pickerItem.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, False) + pickerItem.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, False) + pickerItem.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, False) + pickerItem.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, False) + + # =============================================================== + # #Add Comment data + # =============================================================== + commentData = data["commentData"] + + for item in commentData: + rect = item.get("rect") + color = item.get("color") + label = item.get("label") + tab = item.get("tab") + + tabChildren = tabWidget.children() + + # get a list of the tabs with their names and index + tabInfo = {} + for i in range(len(tabChildren)): + tabName = tabWidget.tabText(i) + tabInfo[tabName] = i + + # now find a matching entry in that list to our tab variable + index = tabInfo.get(tab) + if index is None: + index = 0 + # set out tab widget to that index + tabWidget.setCurrentIndex(index) + + view = self.getCurrentView() + scene = self.getCurrentScene() + + box = interfaceUtils.commentBoxItem(rect[0], rect[1], rect[2], rect[3], scene, view, self) + box.brush.setColor(QtGui.QColor.fromRgb(color[0], color[1], color[2], color[3])) + box.textLabel.setPlainText(label) + scene.addItem(box) + + # =============================================================== + # #Find button color data + # =============================================================== + try: + buttonData = data["buttonData"] + + for border in borderItems: + pickerModule = border[1] + picker = border[0] + networkNode = border[2] + + for button in buttonData: + module = button.get("module") + color = button.get("color") + control = button.get("control") + + if pickerModule == module: + + # then we have the correct border item parent and now need to get its button + childItems = picker.childItems() + for child in childItems: + if type(child) == interfaceUtils.pickerButton: + + # delete the existing scriptJob + scriptJob = picker.data(5) + cmds.scriptJob(kill=scriptJob) + self.selectionScriptJobs.remove(scriptJob) + + # set the button color + child.brush.setColor(QtGui.QColor.fromRgb(color[0], color[1], color[2], color[3])) + + inst = self.getModuleInst(networkNode) + + # create the new scriptJob + newColor = QtGui.QColor.fromRgb(color[0], color[1], color[2], color[3]) + scriptJob = cmds.scriptJob(event=["SelectionChanged", + partial(inst.selectionScriptJob_animUI, + [[child, control, newColor]])], + kws=True) + picker.setData(5, scriptJob) + self.selectionScriptJobs.append(scriptJob) + + except Exception, e: + print e + + # =============================================================== + # #write picker file location to character node + # =============================================================== + if not cmds.objExists(characterNode + ".pickerFile"): + cmds.addAttr(characterNode, ln="pickerFile", dt="string") + + niceFileName = utils.returnFriendlyPath(filename) + pickerFile = niceFileName.partition(utils.returnNicePath(self.toolsPath, "Core/Pickers/") + "/")[2] + cmds.setAttr(characterNode + ".pickerFile", pickerFile, type="string") + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def mirrorChildTextItems(self, children): + """ + Mirrors any QGraphicsSimpleTextItems text if the parent picker was mirrored, so that the text is correct. + :param children: List of child items (QGraphicsSimpleTextItems) of a pickerBorderItem or a pickerButton. + + """ + + # for mirroring text on any child items of a pickerBorderItem or a pickerButton + for child in children: + if type(child) == QtWidgets.QGraphicsSimpleTextItem: + child.setTransformOriginPoint(child.boundingRect().center()) + child.setTransform(QtGui.QTransform(-1.0, 0.0, 0.0, 1.0, child.boundingRect().width(), 0.0)) + + children = child.childItems() + if children is not None: + self.mirrorChildTextItems(children) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getCurrentScene(self): + """ + Gets the QGraphicsScene of the current QGraphicsView, which is gotten by calling on self.getCurrentView. + + :return: Returns the QGraphicsScene under the current QGraphicsView. + + .. seealso:: ART_AnimationUI.getCurrentView + + """ + + view = self.getCurrentView() + scene = view.scene() + return scene + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def getCurrentView(self): + """ + Gets the current QGraphicsView based on the currently selected character tab, and the currently selected + picker tab of said character. + + :return: Returns the QGraphicsView that is currently active. + + """ + # get the current tab index and the widget + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + + # search through the children of the widget until we find the gfxScene + children = widget.children() + for child in children: + if type(child) == QtWidgets.QTabWidget: + tab = child + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + + canvasChildren = canvasWidget.children() + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + + return view + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getPickerTabs(self, saving): + """ + Finds and returns all picker item data, like transforms, scale, x/y coordinates, mirrored status, parent tab, + and controlled module. + + :param saving: Whether or not this function is being called by savePicker, in which case if it is, it will then + make sure that the picker items are no longer editable. + + :rtype: A list of lists, where each inner list has the following data for a picker item: + :return [0]: Name of tab the picker item belongs to + :return [1]: Picker item transforms + :return [2]: Name of module picker belongs to (which module controls it interfaces with) + :return [3]: Whether or not the picker item is mirrored. + :return [4]: Whether or not the picker is a sub-picker, like fingers or toes. + :return [5]: And if not saving, the memory address of the picker item. + + """ + + # get the current tab index and the widget + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + returnData = [] + + # get the children of the current tab widget + children = widget.children() + for child in children: + + # if we find a tab widget, search for the gfxScene + if type(child) == QtWidgets.QTabWidget: + tab = child + selectedTab = tab.currentIndex() + + for i in range(tab.count()): + tab.setCurrentIndex(i) + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + canvasChildren = canvasWidget.children() + + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + # get all items in the gfxScene + itemsInScene = scene.items() + + for item in itemsInScene: + # if we find our top level picker item (the borderItem), get it's data + if type(item) == interfaceUtils.pickerBorderItem or item.type() == 3: + + # item.data(1)could have data that is the scale factor, however, if this is + # a fresh picker, it will not. + scaleFactor = item.data(1) + if scaleFactor is None: + scaleFactor = item.scale + + # get position, rotation, and find out if the pickerBorderItem has been mirrored + position = item.pos() + position = [position.x(), position.y()] + rotation = item.rotation() + mirrored = item.transform().m11() + itemData = {} + + # add data to dictionary + itemData["tab"] = tab.tabText(canvasIndex) + itemData["transforms"] = [position, rotation, scaleFactor] + itemData["module"] = item.data(QtCore.Qt.UserRole) + itemData["mirrored"] = mirrored + + if item.data(2) is None: + itemData["subPicker"] = False + else: + itemData["subPicker"] = True + + if not saving: + itemData["item"] = item + + if saving: + # set flags to False so borderItem is no longer moveable, selectable, etc. + item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, False) + item.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, False) + item.setFlag(QtWidgets.QGraphicsItem.ItemIsFocusable, False) + item.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges, False) + + returnData.append(itemData) + + if type(item) == QtWidgets.QGraphicsPixmapItem: + customImg = scene.property("customImg") + if item == customImg: + filePath = scene.property("filePath") + itemData = {} + + itemData["tab"] = tab.tabText(canvasIndex) + itemData["path"] = filePath + + returnData.append(itemData) + + tab.setCurrentIndex(selectedTab) + + return returnData + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getComments(self, saving=False): + """ + Gets any comment boxes that were created on any picker tabs and returns a list of those items. + :param saving: Whether or not this function is being called from savePicker, in which case, it will disable + edits. + + :rtype: A list of lists where the inner list contains the following data for each comment box found: + :return [0]: The QRect of the comment box, which contains the box dimensions and the x/y coordinates. + :return [1]: The color of the comment box + :return [2]: The name of the tab the comment box is under. + :return [3]: The text label of the comment box. + + """ + + # get the current tab index and the widget + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + returnData = [] + + # get the children of the current tab widget + children = widget.children() + for child in children: + + # if we find a tab widget, search for the gfxScene + if type(child) == QtWidgets.QTabWidget: + tab = child + selectedTab = tab.currentIndex() + + for i in range(tab.count()): + tab.setCurrentIndex(i) + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + canvasChildren = canvasWidget.children() + + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + # get all items in the gfxScene + itemsInScene = scene.items() + + for item in itemsInScene: + + if type(item) == interfaceUtils.commentBoxItem: + + itemData = {} + if not saving: + itemData["item"] = item + + # get comment box position and color + geo = item.boundingRect() + color = item.brush.color().getRgb() + itemData["rect"] = [geo.x(), geo.y(), geo.width(), geo.height()] + itemData["color"] = color + itemData["tab"] = tab.tabText(canvasIndex) + + # get comment box text + children = item.childItems() + for each in children: + if type(each) == QtWidgets.QGraphicsTextItem: + label = each.toPlainText() + itemData["label"] = label + + returnData.append(itemData) + + return returnData + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getButtonColors(self): + """ + Gets joint module picker button colors, which can be edited by the user. + + .. note:: Currently, only the joint module supports users being able to change the button color. + + :rtype: A list of lists where the inner list contains the following data for each joint module found: + :return [0]: The name of the module this picker interfaces with. + :return [1]: The color of the picker button. + :return [2]: The name of the control this button selects. + + """ + + # get the current tab index and the widget + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + returnData = [] + + # get the children of the current tab widget + children = widget.children() + for child in children: + + # if we find a tab widget, search for the gfxScene + if type(child) == QtWidgets.QTabWidget: + tab = child + selectedTab = tab.currentIndex() + + for i in range(tab.count()): + tab.setCurrentIndex(i) + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + canvasChildren = canvasWidget.children() + + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + # get all items in the gfxScene + itemsInScene = scene.items() + + for item in itemsInScene: + if type(item) == interfaceUtils.pickerBorderItem or item.type() == 3: + childButtons = item.childItems() + + # if the picker button is from a leaf module, get color data (only leaf for now) + for child in childButtons: + if type(child) == interfaceUtils.pickerButton: + moduleType = item.data(QtCore.Qt.UserRole) + if "ART_Leaf_Module" in moduleType: + + # get button color + color = child.brush.color().getRgb() + + # store module name, button color + itemData = {} + itemData["module"] = moduleType + itemData["color"] = color + itemData["control"] = child.object + + returnData.append(itemData) + + return returnData + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getModuleInst(self, module): + """ + Takes the given module and instantiates it, returning the memory address for the created instance. + + :param module: The name of the module to instantiate. + + :return: The instance of the instantiated module in memory. + + """ + + modType = cmds.getAttr(module + ".moduleType") + modName = cmds.getAttr(module + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(mod) + + # get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + # find the instance of that module + moduleInst = moduleClass(self, modName) + + return moduleInst + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def changeBackground(self): + """ + Creates an interface allowing a user to change the background image of a picker with these dimensions: (442 x + 600) + + """ + + if cmds.window("pyART_changeBackgroundImg_Win", exists=True): + cmds.deleteUI("pyART_changeBackgroundImg_Win", wnd=True) + + mainWin = QtWidgets.QMainWindow(self.mainWidget) + mainWin.setStyleSheet(self.style) + + mainWin.setMinimumSize(QtCore.QSize(400, 200)) + mainWin.setMaximumSize(QtCore.QSize(400, 200)) + + # set qt object name + mainWin.setObjectName("pyART_changeBackgroundImg_Win") + mainWin.setWindowTitle("Change Background") + + # create the main layout + mainWidget = QtWidgets.QFrame() + mainWin.setCentralWidget(mainWidget) + mainLayout = QtWidgets.QVBoxLayout(mainWidget) + + # scroll area contents + scrollContents = QtWidgets.QFrame() + scrollContents.setStyleSheet("background: transparent;") + scrollLayout = QtWidgets.QVBoxLayout() + scrollLayout.setSpacing(5) + + # find tabs. for each tab, create widget for changing background + tabs = [] + index = self.characterTabs.currentIndex() + widget = self.characterTabs.widget(index) + + # get the children of the current tab widget + children = widget.children() + for child in children: + + # if we find a tab widget, get tab name and item + if type(child) == QtWidgets.QTabWidget: + tab = child + + for i in range(tab.count()): + tab.setCurrentIndex(i) + + # find tab's graphics view + canvas = tab.widget(i) + canvasChildren = canvas.children() + for child in canvasChildren: + if type(child) == QtWidgets.QGraphicsView: + gfxView = child + tabs.append([tab, tab.tabText(i), gfxView]) + + for i in range(len(tabs)): + # create the widget for each tab background + layout = QtWidgets.QHBoxLayout() + scrollLayout.addLayout(layout) + + # which tab combo box + tabComboBox = QtWidgets.QComboBox() + tabComboBox.setMinimumWidth(100) + tabComboBox.setFixedHeight(35) + layout.addWidget(tabComboBox) + tabComboBox.setStyleSheet(self.style) + + tabComboBox.addItem(tabs[i][1]) + + # path location + pathField = QtWidgets.QLineEdit() + layout.addWidget(pathField) + pathField.setReadOnly(True) + pathField.setFixedHeight(35) + pathField.setStyleSheet(self.style) + pathField.setMinimumWidth(160) + pathField.setPlaceholderText("442 x 600 PNG image file..") + + # browse button + button = QtWidgets.QPushButton() + layout.addWidget(button) + button.setMinimumSize(35, 35) + button.setMaximumSize(35, 35) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/fileBrowse.png")) + button.setIconSize(QtCore.QSize(30, 30)) + button.setIcon(icon) + button.setStyleSheet(self.style) + button.clicked.connect(partial(self.backgroundBrowse, pathField, tabs[i][2])) + + # clear button + button = QtWidgets.QPushButton() + layout.addWidget(button) + button.setMinimumSize(35, 35) + button.setMaximumSize(35, 35) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/exit.png")) + button.setIconSize(QtCore.QSize(30, 30)) + button.setIcon(icon) + button.setToolTip("Clear custom background image, resetting back to default.") + button.setStyleSheet(self.style) + button.clicked.connect(partial(self.clearBackground, tabs[i][2])) + + # add everything to the scroll Layout + scrollContents.setLayout(scrollLayout) + scrollArea = QtWidgets.QScrollArea() + mainLayout.addWidget(scrollArea) + scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + scrollArea.setWidgetResizable(False) + scrollArea.setWidget(scrollContents) + + # show + mainWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def backgroundBrowse(self, field, gfxView): + """ + Opens a file browser to select a valid .png background image and then apply it to the specified QGraphicsScene. + + :param field: QLineEdit for the path name to be displayed. + :param gfxView: QGraphicsView to add the background image to. + + """ + + startingDir = utils.returnNicePath(self.toolsPath, "Core/Icons") + if os.path.exists(startingDir): + + filename = cmds.fileDialog2(fm=1, okc="Load Picker", dir=startingDir, ff="*.png") + if filename is not None: + filename = filename[0] + field.setText(utils.returnFriendlyPath(filename)) + + # set background image + pixmap = QtGui.QPixmap() + pixmap.load(utils.returnFriendlyPath(filename)) + scene = gfxView.scene() + gfxItem = scene.addPixmap(pixmap) + gfxItem.setZValue(-10) + scene.setProperty("customImg", gfxItem) + scene.setProperty("filePath", filename) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def clearBackground(self, gfxView): + """ + Removes the background image from the given QGraphicsView. + + :param gfxView: The QGraphicsView whose background image to remove. + + .. seealso:: ART_AnimationUI.changeBackground + + """ + + scene = gfxView.scene() + items = scene.items() + customImg = scene.property("customImg") + + for item in items: + if item == customImg: + scene.removeItem(item) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def exportMotion(self): + """ + Instantiates ART_ExportMotionUI to bring up the tool for exporting animation out to various file formats. + + """ + + import ART_ExportMotionUI + reload(ART_ExportMotionUI) + ART_ExportMotionUI.ART_ExportMotion(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def importMotion(self): + """ + Instantiates ART_ImportMotionUI to bring up the tool for importing FBX animation onto the rig. + + """ + + import ART_ImportMotionUI + reload(ART_ImportMotionUI) + ART_ImportMotionUI.ART_ImportMotion(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def resetRigCtrls(self): + """ + Instantiates ART_ResetModulesUI to bring up the tool for resetting transformations on a selected modules. + Also known as "zeroing out". + + """ + + import ART_ResetModulesUI + reload(ART_ResetModulesUI) + ART_ResetModulesUI.ART_ResetModules(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def selectAllCtrls(self): + """ + Instantiates ART_SelectControlsUI to bring up the tool for selecting specified rig controls for selected + modules. + + """ + + show = True + mods = cmds.getModifiers() + if (mods & 4) > 0: + show = False + + import ART_SelectControlsUI + reload(ART_SelectControlsUI) + ART_SelectControlsUI.ART_SelectControls(self, show) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def matchOverRange(self): + """ + Instantiates ART_MatchOverRangeUI to bring up the tool for matching different rig type over a frame range for + selected modules. + + :Example: + + Matching the IK leg rig controls to the Fk leg rig controls over a frame range of 0-30. + + """ + + import ART_MatchOverRangeUI + reload(ART_MatchOverRangeUI) + ART_MatchOverRangeUI.ART_MatchOverRange(self) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +def run(): + """ + Instantiate the ART_AnimationUI class to build the main interface the animators will interact with. + + :return: instance of the ART_AnimationUI in memory. + + """ + + # create new instance of ART_AnimationUI + gui = ART_AnimationUI(getMainWindow()) + + # Dock Control + allowedAreas = ["left", "right"] + + try: + if cmds.dockControl("pyArtv2AnimToolsDock", q=True, exists=True): + cmds.deleteUI("pyART_AnimTools_Win") + cmds.deleteUI("pyArtv2AnimToolsDock", control=True) + + dock = cmds.dockControl("pyArtv2AnimToolsDock", area="right", content="pyART_AnimTools_Win", + allowedArea=allowedAreas, label="Animation Tools", w=450, h=500) + cmds.refresh(force=True) + cmds.dockControl(dock, edit=True, r=True) + + except Exception, e: + cmds.warning("UI failed to launch.") + print e + + return gui diff --git a/Core/Scripts/Interfaces/ART_BakeOffsetsUI.py b/Core/Scripts/Interfaces/ART_BakeOffsetsUI.py new file mode 100644 index 0000000..86fbe1f --- /dev/null +++ b/Core/Scripts/Interfaces/ART_BakeOffsetsUI.py @@ -0,0 +1,273 @@ +""" +Author: Jeremy Ernst +""" + +from functools import partial + +import maya.cmds as cmds +import System.utils as utils + +from ThirdParty.Qt import QtCore, QtWidgets + + +class ART_BakeOffsets(): + """ + This clas builds a UI that allows the rigger to select modules to bake offset mover values up to the global mover + controls, making the offset movers no longer offset from their global mover parents. It can be found on the rig + creator toolbar with the following icon: + .. image:: /images/bakeOffsetsButton.png + + The full interface looks like this, listing found modules in the scene to select and bake offsets down to. + .. image:: /images/bakeOffsets.png + + """ + + def __init__(self, mainUI): + """ + Instantiates the class, getting the QSettings and then calling on ART_BakeOffsetsUI.buildBakeOffsetsUI to + create the interface. + + :param mainUI: The instance of the Rig Creator UI where this class was called from. + + .. seealso:: ART_BakeOffsetsUI.buildBakeOffsetsUI + + """ + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + # build the UI + self.buildBakeOffsetsUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildBakeOffsetsUI(self): + """ + Builds the interface, finding all modules and listing them for selection. + + """ + + if cmds.window("ART_BakeOffsetsWin", exists=True): + cmds.deleteUI("ART_BakeOffsetsWin", wnd=True) + + # launch a UI to get the name information + self.bakeOffsetsWin = QtWidgets.QMainWindow(self.mainUI) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.bakeOffsetsWin.setStyleSheet(self.style) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.bakeOffsetsWin_mainWidget = QtWidgets.QWidget() + self.bakeOffsetsWin.setCentralWidget(self.bakeOffsetsWin_mainWidget) + + # set qt object name + self.bakeOffsetsWin.setObjectName("ART_BakeOffsetsWin") + self.bakeOffsetsWin.setWindowTitle("Bake Offsets") + + # create the mainLayout for the rig creator UI + self.bakeOffsetsWin_mainLayout = QtWidgets.QVBoxLayout(self.bakeOffsetsWin_mainWidget) + self.bakeOffsetsWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.bakeOffsetsWin.resize(400, 250) + self.bakeOffsetsWin.setSizePolicy(mainSizePolicy) + self.bakeOffsetsWin.setMinimumSize(QtCore.QSize(400, 250)) + self.bakeOffsetsWin.setMaximumSize(QtCore.QSize(400, 250)) + + # create the background + self.bakeOffsetsWin_frame = QtWidgets.QFrame() + self.bakeOffsetsWin_mainLayout.addWidget(self.bakeOffsetsWin_frame) + + # create the layout for the widgets + self.bakeOffsetsWin_widgetLayout = QtWidgets.QHBoxLayout(self.bakeOffsetsWin_frame) + self.bakeOffsetsWin_widgetLayout.setContentsMargins(5, 5, 5, 5) + + # add the QListWidget Frame + self.bakeOffsetsWin_moduleListFrame = QtWidgets.QFrame() + self.bakeOffsetsWin_moduleListFrame.setMinimumSize(QtCore.QSize(265, 200)) + self.bakeOffsetsWin_moduleListFrame.setMaximumSize(QtCore.QSize(265, 200)) + self.bakeOffsetsWin_moduleListFrame.setContentsMargins(20, 0, 20, 0) + + # create the list widget + self.bakeOffsetsWin_moduleList = QtWidgets.QListWidget(self.bakeOffsetsWin_moduleListFrame) + self.bakeOffsetsWin_widgetLayout.addWidget(self.bakeOffsetsWin_moduleListFrame) + self.bakeOffsetsWin_moduleList.setMinimumSize(QtCore.QSize(265, 200)) + self.bakeOffsetsWin_moduleList.setMaximumSize(QtCore.QSize(265, 200)) + self.bakeOffsetsWin_moduleList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.bakeOffsetsWin_moduleList.setSpacing(3) + + # add the layout for the buttons + self.bakeOffsetsWin_buttonLayoutAll = QtWidgets.QVBoxLayout() + self.bakeOffsetsWin_widgetLayout.addLayout(self.bakeOffsetsWin_buttonLayoutAll) + self.bakeOffsetsWin_buttonLayoutAll.setContentsMargins(5, 20, 5, 20) + + # add the selection buttons + self.bakeOffsetsWin_selectionButtonLayout = QtWidgets.QVBoxLayout() + self.bakeOffsetsWin_buttonLayoutAll.addLayout(self.bakeOffsetsWin_selectionButtonLayout) + self.bakeOffsetsWin_selectAllButton = QtWidgets.QPushButton("Select All") + self.bakeOffsetsWin_selectAllButton.setMinimumSize(QtCore.QSize(115, 25)) + self.bakeOffsetsWin_selectAllButton.setMaximumSize(QtCore.QSize(115, 25)) + self.bakeOffsetsWin_selectionButtonLayout.addWidget(self.bakeOffsetsWin_selectAllButton) + self.bakeOffsetsWin_selectAllButton.clicked.connect(self.bakeOffsetsWin_moduleList.selectAll) + self.bakeOffsetsWin_selectAllButton.setObjectName("blueButton") + + self.bakeOffsetsWin_selectNoneButton = QtWidgets.QPushButton("Clear Selection") + self.bakeOffsetsWin_selectNoneButton.setMinimumSize(QtCore.QSize(115, 25)) + self.bakeOffsetsWin_selectNoneButton.setMaximumSize(QtCore.QSize(115, 25)) + self.bakeOffsetsWin_selectionButtonLayout.addWidget(self.bakeOffsetsWin_selectNoneButton) + self.bakeOffsetsWin_selectNoneButton.clicked.connect(self.bakeOffsetsWin_moduleList.clearSelection) + self.bakeOffsetsWin_selectNoneButton.setObjectName("blueButton") + + # spacer + spacerItem = QtWidgets.QSpacerItem(20, 80, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.bakeOffsetsWin_selectionButtonLayout.addItem(spacerItem) + + # add the buttons for reset settings and reset transforms + self.bakeOffsetsWin_bakeOFfsetsBtn = QtWidgets.QPushButton("Bake Offsets") + self.bakeOffsetsWin_bakeOFfsetsBtn.setMinimumSize(QtCore.QSize(115, 25)) + self.bakeOffsetsWin_bakeOFfsetsBtn.setMaximumSize(QtCore.QSize(115, 25)) + self.bakeOffsetsWin_selectionButtonLayout.addWidget(self.bakeOffsetsWin_bakeOFfsetsBtn) + self.bakeOffsetsWin_bakeOFfsetsBtn.setToolTip("Turn on Aim Mode for selected modules.") + self.bakeOffsetsWin_bakeOFfsetsBtn.clicked.connect(partial(self.bakeOffsets)) + self.bakeOffsetsWin_bakeOFfsetsBtn.setObjectName("blueButton") + + # populate the list widget + modules = utils.returnRigModules() + for module in modules: + # get module name + moduleName = cmds.getAttr(module + ".moduleName") + if moduleName != "root": + self.bakeOffsetsWin_moduleList.addItem(moduleName) + + # show the window + self.bakeOffsetsWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def bakeOffsets(self): + """ + Tales selected modules listed in the QListWidget and bakes the offset mover values up to the global mover + parent. This is achieved by creating a locator in the space of the offset mover, zeroing the offset mover + out, and constraining the global mover to the constraint. Finally, the constraints are removed and the + locators deleted. + + """ + + selected = self.bakeOffsetsWin_moduleList.selectedItems() + items = [] + for each in selected: + items.append(each.text()) + + constraints = [] + locators = [] + + # go through each module and create the locators for the offset movers + for each in self.mainUI.moduleInstances: + name = each.name + if name in items: + + # get movers + jointMovers = each.returnJointMovers + + # separate mover lists + globalMovers = jointMovers[0] + offsetMovers = jointMovers[1] + + # create locators for the offsetMovers, then zero out offset mover + for mover in offsetMovers: + locatorName = mover.partition("_offset")[0] + "_loc" + loc = cmds.spaceLocator(name=locatorName)[0] + + # constrain locator + constraint = cmds.parentConstraint(mover, loc)[0] + cmds.delete(constraint) + + # parent locator under a copy of the locatorName + parentLoc = cmds.duplicate(loc)[0] + cmds.parent(loc, parentLoc) + locators.append(parentLoc) + + for mover in offsetMovers: + for attr in [".tx", ".ty", ".tz", ".rx", ".ry", ".rz"]: + try: + cmds.setAttr(mover + attr, 0) + except: + pass + + # now, for each global mover, find child groups that are constrained to them + for each in self.mainUI.moduleInstances: + name = each.name + if name in items: + # get movers + jointMovers = each.returnJointMovers + + # separate mover lists + globalMovers = jointMovers[0] + offsetMovers = jointMovers[1] + + childGrps = [] + + for mover in globalMovers: + try: + + # find the mover group constrained to this mover (if any) + connection = cmds.listConnections(mover, type="parentConstraint")[0] + grp = cmds.listConnections(connection + ".constraintRotateX")[0] + if grp.find("_mover_grp") != -1: + childGrps.append([mover, grp]) + + # now take this group and delete the constraints + cmds.select(grp) + cmds.delete(constraints=True) + + except Exception, e: + print e + + # now snap global movers to offset movers + for each in self.mainUI.moduleInstances: + name = each.name + if name in items: + # get movers + jointMovers = each.returnJointMovers + + # separate mover lists + globalMovers = jointMovers[0] + offsetMovers = jointMovers[1] + + for mover in globalMovers: + if cmds.objExists(mover + "_loc"): + constraint = cmds.parentConstraint(mover + "_loc", mover)[0] + constraints.append(constraint) + + # now remove all locs + for const in constraints: + cmds.delete(const) + + for loc in locators: + cmds.delete(loc) + + # finally, reconstrain mover_grps to their movers + cmds.refresh(force=True) + for group in childGrps: + cmds.parentConstraint(group[0], group[1], mo=True) + cmds.scaleConstraint(group[0], group[1]) + + cmds.select(clear=True) diff --git a/Core/Scripts/Interfaces/ART_BoneCounter.py b/Core/Scripts/Interfaces/ART_BoneCounter.py new file mode 100644 index 0000000..54f9f5b --- /dev/null +++ b/Core/Scripts/Interfaces/ART_BoneCounter.py @@ -0,0 +1,344 @@ +""" +Author: Jeremy Ernst +""" + +from functools import partial + +import maya.cmds as cmds +import System.utils as utils + +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_BoneCounter(): + """ + This class builds a simple interface that allows a user to see what the total bone count of their asset will be + given the current module settings. It also allows the user to set a bone count target. + + It can be found on the Rig Creator toolbar with this icon: + .. image:: /images/boneCounterButton.png + + The interface looks like this: + .. image:: /images/boneCounter.png + + """ + def __init__(self, mainUI): + """ + Instantiates this class, getting the QSettings and calling on the method to build the interface for the tool. + + :param mainUI: The instance of the Rig Creator UI from which this class was called. + + .. seealso:: ART_BoneCounter.buildBoneCounterUI + + """ + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + # build the UI + self.buildBoneCounterUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildBoneCounterUI(self): + """ + Builds the interface for the bone counter tool, which is comprised off a QLineEdit that shows the current + bone count, a QPushButton to launch another simple UI that allows the user to set a bone count target, + and QProgressBar that shows a percentage to target bone count. + + .. seealso:: ART_BoneCounter.setBoneCountTarget + + """ + + if cmds.window("ART_BoneCounterWin", exists=True): + cmds.deleteUI("ART_BoneCounterWin", wnd=True) + + # launch a UI to get the name information + self.boneCounterWin = QtWidgets.QMainWindow(self.mainUI) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.boneCounterWin_mainWidget = QtWidgets.QWidget() + self.boneCounterWin.setCentralWidget(self.boneCounterWin_mainWidget) + + # set qt object name + self.boneCounterWin.setObjectName("ART_BoneCounterWin") + self.boneCounterWin.setWindowTitle("Bone Counter") + + # create the mainLayout for the rig creator UI + self.boneCounterWin_mainLayout = QtWidgets.QVBoxLayout(self.boneCounterWin_mainWidget) + self.boneCounterWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.boneCounterWin.resize(300, 100) + self.boneCounterWin.setSizePolicy(mainSizePolicy) + self.boneCounterWin.setMinimumSize(QtCore.QSize(300, 100)) + self.boneCounterWin.setMaximumSize(QtCore.QSize(300, 100)) + + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + # load toolbar stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + # create the bone counter stylesheet + self.progBarStyle = """ + QProgressBar{ + border: 2px solid black; + border-radius: 5px; + text-align: center; + font: 87 10pt "Arial"; + color: rgb(255,255,255); + } + + QProgressBar::chunk { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(9,62,98)); + width: 10px; + margin: 0.5px; + } + """ + + self.progBarStyleMax = """ + QProgressBar{ + border: 2px solid black; + border-radius: 5px; + text-align: center; + font: 87 10pt "Arial Black"; + color: rgb(255,255,255); + } + + QProgressBar::chunk { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(255,174,0), stop:1 rgb(30,30,30)); + width: 10px; + margin: 0.5px; + } + """ + + # create the background image + self.boneCounterWin_frame = QtWidgets.QFrame() + self.boneCounterWin_mainLayout.addWidget(self.boneCounterWin_frame) + self.boneCounterWin_frame.setObjectName("mid") + self.boneCounterWin.setStyleSheet(self.style) + + # create the layout for the widgets + self.boneCounterWin_widgetLayoutMain = QtWidgets.QVBoxLayout(self.boneCounterWin_frame) + self.boneCounterWin_widgetLayoutMain.setContentsMargins(5, 5, 5, 5) + self.boneCounterWin_widgetLayout = QtWidgets.QHBoxLayout() + self.boneCounterWin_widgetLayoutMain.addLayout(self.boneCounterWin_widgetLayout) + + # label creation + self.boneCount = QtWidgets.QLabel("Bone Count: ") + self.boneCount.setFont(headerFont) + self.boneCounterWin_widgetLayout.addWidget(self.boneCount) + + self.boneCounter = QtWidgets.QSpinBox() + self.boneCounter.setReadOnly(True) + self.boneCounter.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.boneCounter.setRange(1, 9999) + self.boneCounter.setMinimumWidth(60) + self.boneCounter.setMaximumWidth(60) + self.boneCounterWin_widgetLayout.addWidget(self.boneCounter) + + # create the button + self.boneMaxButton = QtWidgets.QPushButton("Set Target") + self.boneCounterWin_widgetLayout.addWidget(self.boneMaxButton) + self.boneMaxButton.clicked.connect(partial(self.setBoneCountTarget)) + self.boneMaxButton.setMinimumHeight(30) + self.boneMaxButton.setStyleSheet(self.style) + self.boneMaxButton.setObjectName("blueButton") + + # add the progress bar + self.boneCountBar = QtWidgets.QProgressBar() + self.boneCounterWin_widgetLayoutMain.addWidget(self.boneCountBar) + self.boneCountBar.setValue(1) + self.boneCountBar.setStyleSheet(self.progBarStyle) + self.boneCountBar.setRange(0, 100) + self.boneCountBar.setFormat("target = %m") + + # add the max range of the progress bar to the main network node + if cmds.objExists("ART_Root_Module.target"): + value = cmds.getAttr("ART_Root_Module.target") + currentValue = self.boneCountBar.value() + self.boneCountBar.setMaximum(value) + + if value < currentValue: + self.boneCountBar.setValue(value) + + if currentValue <= value: + self.boneCountBar.setStyleSheet(self.progBarStyle) + if currentValue > value: + self.boneCountBar.setStyleSheet(self.progBarStyleMax) + + else: + cmds.addAttr("ART_Root_Module", sn="target", keyable=False) + cmds.setAttr("ART_Root_Module.target", 100, lock=True) + + # get the current bone count + modules = utils.returnRigModules() + allBones = [] + for module in modules: + joints = cmds.getAttr(module + ".Created_Bones") + splitJoints = joints.split("::") + + for bone in splitJoints: + if bone != "": + allBones.append(bone) + + # update the spinBox and progress bar + self.boneCounter.setValue(len(allBones)) + max = self.boneCountBar.maximum() + + if len(allBones) <= max: + self.boneCountBar.setValue(len(allBones)) + self.boneCountBar.setStyleSheet(self.progBarStyle) + if len(allBones) > max: + self.boneCountBar.setValue(max) + self.boneCountBar.setStyleSheet(self.progBarStyleMax) + + # show window + self.boneCounterWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def setBoneCountTarget(self): + """ + Builds a UI that allows the user to set the target bone count. + + .. image:: /images/boneCountTarget.png + + """ + + # launch a UI to get the name information + self.targetWindow = QtWidgets.QMainWindow() + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.targetWindow_mainWidget = QtWidgets.QWidget() + self.targetWindow.setCentralWidget(self.targetWindow_mainWidget) + + # set qt object name + self.targetWindow.setObjectName("ART_setBoneCountTarget_UI") + self.targetWindow.setWindowTitle("Bone Count") + + # create the mainLayout for the rig creator UI + self.targetWindow_topLayout = QtWidgets.QVBoxLayout(self.targetWindow_mainWidget) + self.targetWindow_mainLayout = QtWidgets.QFormLayout() + self.targetWindow_topLayout.addLayout(self.targetWindow_mainLayout) + + self.targetWindow_topLayout.setContentsMargins(10, 10, 10, 10) + self.targetWindow.resize(250, 70) + self.targetWindow.setSizePolicy(mainSizePolicy) + self.targetWindow.setMinimumSize(QtCore.QSize(250, 70)) + self.targetWindow.setMaximumSize(QtCore.QSize(250, 70)) + + # add label + label = QtWidgets.QLabel("Enter a target bone count: ") + self.targetWindow_mainLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, label) + + # add spinBox + self.targetWindow_SpinBox = QtWidgets.QSpinBox() + self.targetWindow_SpinBox.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.targetWindow_SpinBox.setRange(1, 9999) + self.targetWindow_SpinBox.setMinimumWidth(70) + self.targetWindow_SpinBox.setStyleSheet("background-color: rgb(255,255,255); color: rgb(0,0,0);") + self.targetWindow_mainLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.targetWindow_SpinBox) + self.targetWindow_SpinBox.setValue(100) + self.targetWindow_SpinBox.setObjectName("ART_targetWindowSpinBox") + + # add a confirm button + self.targetWindow_confirmButton = QtWidgets.QPushButton("Confirm") + self.targetWindow_topLayout.addWidget(self.targetWindow_confirmButton) + + buttonImage = utils.returnNicePath(self.iconsPath, "System/blue_field_background.png") + self.targetWindow_confirmButton.setStyleSheet( + "background-image: url(" + buttonImage + "); background-color: rgb(0,0,0);") + self.targetWindow_confirmButton.clicked.connect(self.setBoneCountTarget_Confirm) + + # show the window + self.targetWindow.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def setBoneCountTarget_Confirm(self): + """ + Take the QSpinBox value for the new target and refresh the main UI with the new data. + + """ + + newMax = self.targetWindow_SpinBox.value() + cmds.deleteUI("ART_setBoneCountTarget_UI", wnd=True) + + value = self.boneCounter.value() + currentValue = self.boneCountBar.value() + self.boneCountBar.setMaximum(newMax) + max = self.boneCountBar.maximum() + + cmds.setAttr("ART_Root_Module.target", lock=False) + cmds.setAttr("ART_Root_Module.target", newMax, lock=True) + + if newMax < currentValue: + self.boneCountBar.setValue(max) + + if value <= max: + self.boneCountBar.setStyleSheet(self.progBarStyle) + if value > max: + self.boneCountBar.setStyleSheet(self.progBarStyleMax) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def updateBoneCount(self): + """ + Updates the interface based on new module information. Usually triggered when a module has had its settings + changed. + + """ + + try: + modules = utils.returnRigModules() + allBones = [] + for module in modules: + joints = cmds.getAttr(module + ".Created_Bones") + splitJoints = joints.split("::") + + for bone in splitJoints: + if bone != "": + allBones.append(bone) + + # update the spinBox and progress bar + self.boneCounter.setValue(len(allBones)) + max = self.boneCountBar.maximum() + + if len(allBones) <= max: + self.boneCountBar.setValue(len(allBones)) + self.boneCountBar.setStyleSheet(self.progBarStyle) + if len(allBones) > max: + self.boneCountBar.setValue(max) + self.boneCountBar.setStyleSheet(self.progBarStyleMax) + except: + pass diff --git a/Core/Scripts/Interfaces/ART_BuildProgressUI.py b/Core/Scripts/Interfaces/ART_BuildProgressUI.py new file mode 100644 index 0000000..809c2d1 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_BuildProgressUI.py @@ -0,0 +1,835 @@ +""" +Author: Jeremy Ernst +""" + +import os + +import maya.cmds as cmds +import maya.mel as mel +import System.utils as utils + +import System.interfaceUtils as interfaceUtils +import System.riggingUtils as riggingUtils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_BuildProgress_UI(): + """ + This class kicks off building all of the rigs for the modules. It displays an interface that shows progress and + displays information about the rig build. + + It exports skin weights, rebuilds the skeleton in rig pose, imports skin weights, runs any pre-script, + calls on each module's rig building code, sets up rig parenting and hierarchy, and finally runs any post-script. + + .. image:: /images/buildProgress.gif + + """ + + def __init__(self, mainUI): + """ + Instantiates the class, getting the QSettings and then calling on the function to build the UI for the tool. + + :param mainUI: The instance of the Rig Creator UI that this class was called from. + + .. seealso:: ART_BuildProgressUI.buildUI + + """ + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.projectPath = settings.value("projectPath") + self.iconsPath = settings.value("iconPath") + + self.mainUI = mainUI + self.rigData = [] + self.warnings = 0 + self.errors = 0 + + # images + self.imageBkgrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/toolbar_background.png")) + self.imageBtnBkrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/blue_field_background.png")) + self.frameBackground = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/field_background.png")) + + # build the UI + if cmds.window("ART_BuildProgressWin", exists=True): + cmds.deleteUI("ART_BuildProgressWin", wnd=True) + + self.buildUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + """ + Builds the interface, which doesn't really have any user-interaction, but is there to display information + about the progress of the rig build. There are two QProgressBars that show current module build progress and + total build progress, then a QTextEdit which outputs information about what the build process is currently + working on. + + After the interface is built, it sets the rig pose on the joints of each module. + + """ + + # create the main window + self.mainWin = QtWidgets.QMainWindow(interfaceUtils.getMainWindow()) + self.mainWin.setStyleSheet("background-color: rgb(0, 0, 0);, color: rgb(0,0,0);") + + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.mainWin.setStyleSheet(self.style) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + # set qt object name + self.mainWin.setObjectName("ART_BuildProgressWin") + self.mainWin.setWindowTitle("Build Progress") + + # font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + # set size policy + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + self.mainWin.resize(500, 300) + self.mainWin.setSizePolicy(mainSizePolicy) + self.mainWin.setMinimumSize(QtCore.QSize(500, 300)) + self.mainWin.setMaximumSize(QtCore.QSize(500, 300)) + + # create the QFrame for this page + self.background = QtWidgets.QFrame() + self.layout.addWidget(self.background) + self.mainLayout = QtWidgets.QVBoxLayout(self.background) + self.background.setObjectName("epic") + + # build the progress bars: + + self.currentTask = QtWidgets.QProgressBar() + self.mainLayout.addWidget(self.currentTask) + self.currentTask.setRange(0, 100) + self.currentTask.setTextVisible(True) + self.currentTask.setValue(0) + + self.totalProgress = QtWidgets.QProgressBar() + self.mainLayout.addWidget(self.totalProgress) + self.totalProgress.setFormat("Total Progress..") + self.totalProgress.setRange(0, 12) + self.totalProgress.setTextVisible(True) + self.totalProgress.setValue(0) + + # detailed information + self.infoText = QtWidgets.QTextEdit() + self.mainLayout.addWidget(self.infoText) + self.infoText.setMinimumHeight(200) + self.infoText.setMaximumHeight(200) + self.infoText.setText("Starting Build Process..") + self.infoText.setReadOnly(True) + + # show the window + self.mainWin.show() + + # start build + self.setRigPose() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def exportWeights(self): + """ + Exports all skin weights of meshes that have skinClusters to a .weights file (JSON). It also has + functionality to deal with morph targets, making sure they are preserved when history on the meshes is deleted. + + .. seealso:: riggingUtils.export_skin_weights() + + """ + + self.infoText.append("\n") + self.infoText.append("|| EXPORTING SKIN WEIGHTS ||") + + # find meshes that are weighted + weightedMeshes = [] + skinClusters = cmds.ls(type='skinCluster') + + for cluster in skinClusters: + geometry = cmds.skinCluster(cluster, q=True, g=True)[0] + geoTransform = cmds.listRelatives(geometry, parent=True)[0] + weightedMeshes.append([geoTransform, cluster]) + + # update progress bar + numMeshes = len(weightedMeshes) + self.currentTask.setRange(0, numMeshes) + self.currentTask.setValue(0) + + # save out weights of meshes + for mesh in weightedMeshes: + filePath = utils.returnFriendlyPath(os.path.join(cmds.internalVar(utd=True), mesh[0] + ".WEIGHTS")) + + # export skin weights + riggingUtils.export_skin_weights(filePath, mesh[0]) + + # CHECK FOR MORPH TARGETS + blendshapeList = [] + + # find blendshapes + skinCluster = riggingUtils.findRelatedSkinCluster(mesh[0]) + + if skinCluster is not None: + blendshapes = cmds.listConnections(skinCluster + ".input", source=True, type="blendShape") + deleteShapes = [] + if blendshapes is not None: + for each in blendshapes: + attrs = cmds.listAttr(each, m=True, string="weight") + if attrs is not None: + for attr in attrs: + + # if not, manually create shapes by toggling attrs and duplicating mesh + if not cmds.objExists(attr): + cmds.setAttr(each + "." + attr, 1) + dupe = cmds.duplicate(mesh[0])[0] + + # parent to world + parent = cmds.listRelatives(dupe, parent=True) + if parent is not None: + cmds.parent(dupe, world=True) + + # rename the duplicate mesh to the blendshape name + cmds.rename(dupe, attr) + cmds.setAttr(each + "." + attr, 0) + deleteShapes.append(attr) + + # add the blendshape node name and its attrs to the master blendshape list + blendshapeList.append([each, attrs]) + + # delete history of meshes + cmds.delete(mesh[0], ch=True) + + # reapply blendshapes + for item in blendshapeList: + bshapeName = item[0] + shapeList = item[1] + + i = 1 + for shape in shapeList: + if cmds.objExists(bshapeName): + cmds.blendShape(bshapeName, edit=True, t=(mesh[0], i, shape, 1.0)) + + else: + cmds.select([shape, mesh[0]], r=True) + cmds.blendShape(name=bshapeName) + cmds.select(clear=True) + + try: + for each in deleteShapes: + cmds.delete(each) + except: + pass + + # update progress and info + self.infoText.append(" Exported Skin Weights for " + mesh[0]) + curVal = self.currentTask.value() + self.currentTask.setValue(curVal + 1) + + # update main progress bar + self.totalProgress.setValue(1) + self.infoText.append("\n") + + # rebuild the skeleton in rig pose + self.rebuildSkeleton() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def setRigPose(self): + """ + Sets the rig pose on each module's joints. Lastly, calls on ART_BuildProgressUI.exportWeights() + + ..seealso :: ART_BuildProgressUI.exportWeights() + + """ + + # set rig pose + self.infoText.append("Setting Rig Pose..") + numMods = len(self.mainUI.moduleInstances) - 1 + + self.currentTask.setRange(0, numMods) + self.currentTask.setValue(0) + + for inst in self.mainUI.moduleInstances: + if inst.name != "root": + inst.setupForRigPose() + inst.setReferencePose("rigPose") + inst.cleanUpRigPose() + curVal = self.currentTask.value() + self.currentTask.setValue(curVal + 1) + + # update main progress bar + self.totalProgress.setValue(2) + + self.exportWeights() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def rebuildSkeleton(self): + """ + Rebuilds the skeleton in rig pose, meaning that all joint rotations will be zeroed in rig pose rather than + model pose. This ensures clean rigging. + + Lastly, calls on ART_BuildProgressUI.importWeights() to reimport weighting back onto the meshes. + + .. seealso:: riggingUtils.buildSkeleton(), ART_BuildProgressUI.importWeights() + + """ + + # rebuild the skeleton + cmds.delete("root") + + self.infoText.append("Rebuilding Skeleton in Rig Pose..") + + # build skeleton from utils + riggingUtils.buildSkeleton() + + # update main progress bar + self.totalProgress.setValue(3) + + # import weights + self.importWeights() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def importWeights(self): + """ + Imports skin weights back onto the asset geometry after having rebuilt the skeleton in rig pose. Then calls + on ART_BuildProgressUI.preScript(). + + .. seealso:: ART_BuildProgressUI.preScript() + + """ + + meshes = utils.findAllSkinnableGeo() + + self.currentTask.setRange(0, len(meshes)) + self.currentTask.setValue(0) + self.infoText.append("\n") + self.infoText.append("|| IMPORTING SKIN WEIGHTS ||") + + for mesh in meshes: + filePath = utils.returnFriendlyPath(os.path.join(cmds.internalVar(utd=True), mesh + ".WEIGHTS")) + + if os.path.exists(filePath): + riggingUtils.import_skin_weights(filePath, mesh, True) + + # update progress and info + self.infoText.append(" Imported Skin Weights for " + mesh) + curVal = self.currentTask.value() + self.currentTask.setValue(curVal + 1) + + # remove skin file + os.remove(filePath) + + else: + # update progress and info + self.infoText.setTextColor(QtGui.QColor(236, 217, 0)) + self.infoText.append(" Could not import weights for " + mesh) + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + self.warnings += 1 + + curVal = self.currentTask.value() + self.currentTask.setValue(curVal + 1) + + # update main progress bar + self.totalProgress.setValue(5) + + # call on the prescript + self.preScript() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # def exportMeshes(self): + # + # lodAttrs = utils.getLodData() + # exportData = utils.findExportMeshData() + # + # self.currentTask.setRange(0, len(lodAttrs)) + # self.currentTask.setValue(0) + # + # + # #save the file + # saveFile = cmds.file(q = True, sceneName = True) + # + # + # try: + # cmds.file(save = True) + # except Exception, e: + # cmds.error("Could not save file. Error: " + str(e)) + # return + # + # + # #for each LOD + # for each in exportData: + # meshValue = each[1] + # pathValue = each[0] + # boneValue = each[2] + # poseData = each[3] + # utils.exportMesh(self.mainUI, meshValue, pathValue, boneValue, poseData) + # + # #open the file + # cmds.file(saveFile, open = True, force = True) + # + # #update UI + # self.infoText.setTextColor(QtGui.QColor(0,255,18)) + # self.infoText.append(" SUCCESS: FBX file exported.") + # self.infoText.append(" " + str(pathValue)) + # self.infoText.setTextColor(QtGui.QColor(255,255,255)) + # + # #update progress bar + # curVal = self.currentTask.value() + # self.currentTask.setValue(curVal + 1) + # + # #update main progress bar + # self.totalProgress.setValue(7) + # + # #run pre-script + # self.preScript() + + """ + Keeping this around for now, but currently it is not being used. + Exporting is now done through edit rig -> Export Skeletal Meshes + """ + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def preScript(self): + """ + If there is a pre-script to run, this will call on ART_BuildProgressUI.executeScript() to run the pre-script. + Then it will call on ART_BuildProgressUI.buildRigs() to build each module's rigs. + + .. note:: Pre-Scipts are used if you ever want to do something to your character before the rig gets built. + An example usage would be adding IK joints for UE4 in a pre-script, as you don't want or need + controls for those IK joints, and setting up those constraints. + + .. seealso:: ART_BuildProgressUI.executeScript(), ART_BuildProgressUI.buildRigs() + + """ + + self.infoText.append(" \n") + + # get pre-script path from character node, if it exists + characterNode = utils.returnCharacterModule() + if cmds.objExists(characterNode + ".preScriptPath"): + scriptPath = cmds.getAttr(characterNode + ".preScriptPath") + self.infoText.append("Executing Pre-Script..") + self.infoText.append(" " + scriptPath) + + # try to execute the pre-script + status = self.executeScript(scriptPath) + if status: + self.infoText.setTextColor(QtGui.QColor(0, 255, 18)) + self.infoText.append(" SUCCESS: Pre-Script Was Successfully Executed..") + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + + if not status: + self.infoText.setTextColor(QtGui.QColor(255, 0, 0)) + self.infoText.append(" FAILED: Pre-Script Was Not Successfully Executed..") + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + self.errors += 1 + else: + self.infoText.setTextColor(QtGui.QColor(255, 255, 0)) + self.infoText.append("No Pre-Script To Run..") + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + + # update main progress bar + self.totalProgress.setValue(8) + + # build rigs + self.buildRigs() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildRigs(self): + """ + First, create a driver skeleton which the rig will build upon, then calls on each module's buildCustomRig + function, which will build the rig for that module, then sets up rig parenting and hiearchy once all modules + are built. + + Lastly, calls on the function to execute a post script if one was given during publish. + + .. seealso:: riggingUtils.createDriverSkeleton(), ART_BuildProgressUI.postScript() + + """ + + self.infoText.append(" \n") + self.infoText.append("|| PREPARING TO BUILD CONTROL RIGS.. ||") + + # Update Current Task bar with num modules + self.currentTask.setRange(0, len(self.mainUI.moduleInstances)) + self.currentTask.setValue(0) + + # create the driver skeleton + riggingUtils.createDriverSkeleton() + + # Loop through modules, building rigs + for inst in self.mainUI.moduleInstances: + self.infoText.append(" \n") + self.infoText.append(" Building: " + str(inst.name)) + + # build module rigs + inst.buildRig(self.infoText, self) + + # update progress bar + curVal = self.currentTask.value() + self.currentTask.setValue(curVal + 1) + + # ======================================================================= + # #Setup the rig parenting + # ======================================================================= + for data in self.rigData: + createdConstraints = [] + + if data[1] == "driver_root": + pConst = cmds.pointConstraint("offset_anim", data[0], mo=True)[0] + oConst = cmds.orientConstraint("offset_anim", data[0], mo=True)[0] + if pConst not in createdConstraints: + createdConstraints.append(pConst) + if oConst not in createdConstraints: + createdConstraints.append(oConst) + + else: + # get the connections of the passed in parent bone + connections = [] + for connection in cmds.listConnections(data[1], type="constraint"): + connections.append(connection) + + for connection in list(set(connections)): + constraintType = cmds.nodeType(connection) + driveAttrs = [] + targetWeights = [] + + # get those constraint target attributes for each constraint connection + targets = cmds.getAttr(connection + ".target", mi=True) + if len(targets) >= 1: + for each in targets: + # get the names of the constraint targets + targetWeights.append( + cmds.listConnections(connection + ".target[" + str(each) + "].targetWeight", + p=True)[0]) + for targetWeight in targetWeights: + name = targetWeight.rpartition(".")[2] + name = name.rpartition("W")[0] + if name not in driveAttrs: + driveAttrs.append(name) + + # Setup constraint between the driveAttrs and the rigData[0] node + for attr in driveAttrs: + print attr, data[0] + if constraintType == "pointConstraint": + try: + pConst = cmds.pointConstraint(attr, data[0], mo=True)[0] + if pConst not in createdConstraints: + createdConstraints.append(pConst) + except Exception, e: + cmds.warning(str(e)) + + if constraintType == "orientConstraint": + try: + oConst = cmds.orientConstraint(attr, data[0], mo=True)[0] + if oConst not in createdConstraints: + createdConstraints.append(oConst) + except Exception, e: + cmds.warning(str(e)) + + if constraintType == "parentConstraint": + try: + paConst = cmds.parentConstraint(attr, data[0], mo=True)[0] + if paConst not in createdConstraints: + createdConstraints.append(paConst) + except Exception, e: + cmds.warning(str(e)) + + # get the names of the constraint targets + for const in createdConstraints: + constraintAttrs = [] + targets = cmds.getAttr(const + ".target", mi=True) + for each in targets: + constraintAttrs.append( + cmds.listConnections(const + ".target[" + str(each) + "].targetWeight", p=True)[0]) + + # setup connections between the parent bone constraints and the newly created constraints + if len(constraintAttrs) > 0: + for i in range(len(constraintAttrs)): + try: + cmds.connectAttr(targetWeights[i], constraintAttrs[i]) + except: + pass + + # ======================================================================= + # #set the state on the main network node + # ======================================================================= + if cmds.objExists("ART_RIG_ROOT.state"): + cmds.setAttr("ART_RIG_ROOT.state", 2) + + # ======================================================================= + # #hide all joints + # ======================================================================= + joints = cmds.ls(type="joint") + for joint in joints: + try: + cmds.setAttr(joint + ".v", lock=False) + cmds.setAttr(joint + ".v", 0, lock=True) + except: + pass + + # ======================================================================= + # #remove unused skin influences to optimize scene + # ======================================================================= + mel.eval("removeAllUnusedSkinInfs()") + + # ======================================================================= + # #save the file + # ======================================================================= + cmds.file(save=True, type="mayaAscii") + + # update main progress bar + self.totalProgress.setValue(9) + + # ======================================================================= + # #execute post script + # ======================================================================= + self.postScript() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def postScript(self): + """ + If there is a post-script to run, this will call on ART_BuildProgressUI.executeScript() to run the post-script. + Then it will call on ART_BuildProgressUI.completeBuild() which wraps up the build process and alerts the user + the build is done. + + .. note:: Post-Scipts are used if you ever want to do something to your character after the rig gets built. + An example usage would be adding custom rigging to joints or controls in the scene, setting up custom + relationships or set-driven keys, etc. + + .. seealso:: ART_BuildProgressUI.executeScript(), ART_BuildProgressUI.completeBuild() + + """ + + self.infoText.append(" \n") + + # get pre-script path from character node, if it exists + characterNode = utils.returnCharacterModule() + if cmds.objExists(characterNode + ".postScriptPath"): + scriptPath = cmds.getAttr(characterNode + ".postScriptPath") + self.infoText.append("Executing Post-Script..") + self.infoText.append(" " + scriptPath) + + # try to execute the pre-script + status = self.executeScript(scriptPath) + if status: + self.infoText.setTextColor(QtGui.QColor(0, 255, 18)) + self.infoText.append(" SUCCESS: Post-Script Was Successfully Executed..") + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + + if not status: + self.infoText.setTextColor(QtGui.QColor(255, 0, 0)) + self.infoText.append(" FAILED: Post-Script Was Not Successfully Executed..") + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + self.errors += 1 + + else: + self.infoText.setTextColor(QtGui.QColor(255, 255, 0)) + self.infoText.append("No Post-Script To Run..") + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + + # update main progress bar + self.totalProgress.setValue(10) + + # capture model pose for rig controls + cmds.file(save=True, type="mayaAscii") + self.completeBuild() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def completeBuild(self): + """ + Locks down all network nodes, saves the scene, and alerts user that the rig build is complete. + + """ + + self.infoText.append(" \n") + self.infoText.append("Cleaning Up..") + + # go through each module instance, and lock down the nodes + cmds.select("rig_grp", hi=True) + rigNodes = cmds.ls(sl=True) + numNodes = len(rigNodes) + self.currentTask.setMaximum(numNodes) + + for node in rigNodes: + curVal = self.currentTask.value() + + try: + cmds.lockNode(node, lock=True) + self.currentTask.setValue(curVal + 1) + + except: + pass + + # save scene + cmds.file(save=True, type="mayaAscii") + + # iterate total progress + self.totalProgress.setValue(12) + + # add build info + font = QtGui.QFont() + font.setPointSize(20) + font.setBold(True) + + self.infoText.setFont(font) + self.infoText.setTextColor(QtGui.QColor(0, 255, 18)) + self.infoText.append("\n\nPUBLISH COMPLETE!") + + font.setPointSize(8) + font.setBold(False) + self.infoText.setTextColor(QtGui.QColor(255, 255, 255)) + self.infoText.setFont(font) + + # get file name + fileName = cmds.file(q=True, sceneName=True) + iconPath = cmds.getAttr("ART_RIG_ROOT.iconPath") + + self.infoText.append("Assets Created: ") + self.infoText.append(" " + fileName) + self.infoText.append(" " + iconPath) + self.infoText.append(str(self.warnings) + " warnings") + self.infoText.append(str(self.errors) + " errors") + + # tell user build is complete + msgBox = QtWidgets.QMessageBox() + msgBox.setIcon(QtWidgets.QMessageBox.Information) + msgBox.setText("Rig Build Complete!") + msgBox.addButton("New Scene", QtWidgets.QMessageBox.YesRole) + msgBox.addButton("Edit Rig", QtWidgets.QMessageBox.NoRole) + ret = msgBox.exec_() + + if ret == 1: + import ART_RigCreatorUI as ART_RigCreatorUI + reload(ART_RigCreatorUI) + ART_RigCreatorUI.createUI() + cmds.refresh(force=True) + cmds.dockControl("pyArtRigCreatorDock", e=True, r=True) + + else: + + # if the rigCreatorUI exists delete UI + if cmds.dockControl("pyArtRigCreatorDock", q=True, exists=True): + cmds.deleteUI("pyArtRigCreatorUi") + cmds.deleteUI("pyArtRigCreatorDock", control=True) + + cmds.file(new=True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def executeScript(self, scriptPath): + """ + Takes a given script (mel or python) and runs it. + + :param scriptPath: location of the script to be evaluated and ran. + + :return: Whether or not the execution of the script failed or not. + + .. seealso:: ART_BuildProgressUI.preScript(), ART_BuildProgressUI.postScript() + + """ + + sourceType = "" + status = False + + if scriptPath.find(".py") != -1: + sourceType = "python" + + if scriptPath.find(".mel") != -1: + sourceType = "mel" + + # MEL + if sourceType == "mel": + try: + command = "" + # open the file, and for each line in the file, add it to our command string. + f = open(scriptPath, 'r') + lines = f.readlines() + for line in lines: + command += line + + import maya.mel as mel + mel.eval(command) + + # save the file + cmds.file(save=True, type="mayaAscii") + status = True + except: + pass + + # PYTHON + if sourceType == "python": + try: + execfile("" + scriptPath + "") + + # save the file + cmds.file(save=True, type="mayaAscii") + status = True + except: + pass + + return status diff --git a/Core/Scripts/Interfaces/ART_ChangeModuleNameUI.py b/Core/Scripts/Interfaces/ART_ChangeModuleNameUI.py new file mode 100644 index 0000000..e49b0f6 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ChangeModuleNameUI.py @@ -0,0 +1,365 @@ +""" +Author: Jeremy Ernst +""" + +import maya.cmds as cmds + +import System.interfaceUtils as interfaceUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + +# maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + + +def getMainWindow(): + """ + Returns a pointer to Maya's window as a QWidget. + + """ + + import maya.OpenMayaUI as mui + pointer = mui.MQtUtil.mainWindow() + # pyside QMainWindow takes in a QWidget rather than QObject + return shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + + +windowTitle = "Change Module Name" +windowObject = "pyArtChangeModuleNameUi" + + +class ART_ChangeModuleName_UI(QtWidgets.QMainWindow): + """ + This class allows the user to change the prefix or suffix or both, of a given module. It is found within the + skeletonSettingsUI of an individual module in the Rig Creator. + + .. image:: /images/changeModName.png + + """ + + def __init__(self, baseName, moduleInst, rigUiInst, prefix, suffix, parent=None): + """ + Instantiates the class, taking in current module information, and builds the interface. + + :param baseName: The base name of the module, found on the network node attribute. + :param moduleInst: The instance in memory of the module whose name is to change. + :param rigUiInst: The instance in memory of the Rig Creator UI from which this class was called. + :param prefix: The existing prefix of the module name. + :param suffix: The existing suffix of the module name. + + """ + + super(ART_ChangeModuleName_UI, self).__init__(parent) + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + # create class variables + self.baseName = baseName + self.modInst = moduleInst + self.rigUiInst = rigUiInst + self.prefixInc = prefix + self.suffixInc = suffix + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + style = f.read() + f.close() + + self.setStyleSheet(style) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + # set qt object name + self.setObjectName(windowObject) + self.setWindowTitle(windowTitle) + + # create the mainLayout for the rig creator UI + self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) + self.mainLayout.setContentsMargins(0, 0, 0, 0) + + self.resize(300, 150) + self.setSizePolicy(mainSizePolicy) + self.setMinimumSize(QtCore.QSize(300, 150)) + self.setMaximumSize(QtCore.QSize(300, 150)) + + # create the background image + self.frame = QtWidgets.QFrame() + self.mainLayout.addWidget(self.frame) + + # create the layout for the widgets + self.widgetLayout = QtWidgets.QVBoxLayout(self.frame) + + # create the prefix pair of fields + self.prefixForm = QtWidgets.QFormLayout() + self.widgetLayout.addLayout(self.prefixForm) + + self.prefixLabel = QtWidgets.QLabel("Prefix: ") + self.prefixForm.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.prefixLabel) + + self.prefix = QtWidgets.QLineEdit(self.prefixInc) + self.prefixForm.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.prefix) + + # hookup signal/slot connection + self.prefix.textChanged.connect(self.updatePreview) + + # create the suffix pair of fields + self.suffixForm = QtWidgets.QFormLayout() + self.widgetLayout.addLayout(self.suffixForm) + + self.suffixLabel = QtWidgets.QLabel("Suffix: ") + self.suffixForm.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.suffixLabel) + + self.suffix = QtWidgets.QLineEdit(self.suffixInc) + self.suffixForm.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.suffix) + + # hookup signal/slot connection + self.suffix.textChanged.connect(self.updatePreview) + + # spacer + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.widgetLayout.addItem(spacerItem) + + # realtime preview of final module name + self.previewForm = QtWidgets.QFormLayout() + self.widgetLayout.addLayout(self.previewForm) + self.previewLabel = QtWidgets.QLabel("Preview: ") + self.previewName = QtWidgets.QLabel(self.prefixInc + self.baseName + self.suffixInc) + self.previewName.setMinimumSize(QtCore.QSize(200, 20)) + self.previewName.setMaximumSize(QtCore.QSize(200, 20)) + self.previewName.setAlignment(QtCore.Qt.AlignHCenter) + self.previewForm.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.previewLabel) + self.previewForm.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.previewName) + + # set preview font + font = QtGui.QFont() + font.setPointSize(12) + self.previewName.setFont(font) + + spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.widgetLayout.addItem(spacerItem1) + + # update button + self.updateBtn = QtWidgets.QPushButton("UPDATE") + self.widgetLayout.addWidget(self.updateBtn) + self.updateBtn.setMinimumSize(QtCore.QSize(285, 40)) + self.updateBtn.setMaximumSize(QtCore.QSize(285, 40)) + self.updateBtn.setSizePolicy(mainSizePolicy) + font = QtGui.QFont() + font.setPointSize(12) + self.updateBtn.setFont(font) + self.updateBtn.setObjectName("blueButton") + + # hookup signal/slot on create button + self.updateBtn.clicked.connect(self.applyModuleNameChange) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def updatePreview(self): + """ + Updates the QLabel with the current prefix + basename + suffix, adding in underscores where needed. + + """ + + prefix = str(self.prefix.text()) + suffix = str(self.suffix.text()) + + string = "" + if len(prefix) > 0: + string += prefix + "_" + + string += self.baseName + + if len(suffix) > 0: + string += "_" + suffix + + self.previewName.setText(string) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def applyModuleNameChange(self): + """ + Checks to make sure a module doesn't exist with the new name, and if not, updates naming of the module in a + multitude of places. Any UI elements, joint movers, attribute values (like .Created_Bones), etc. + + .. note:: + The following things get updated: + * QGroupBox label of the SkeletonSettingsUI + * Network Node .moduleName attribute + * Module Instance variable of self.name gets updated + * Created_Bones attribute values + * Joint Mover Nodes + * Rig Creator Outliner names for module + * Selection Script Job for outliner + * Any modules' attributes that have a value that matches the old name (like parent module bone) + * Any modules that are a mirror of this module, and their mirrorModule attribute value + + """ + + # check to see if a module already has that name. If so, return out and do not continue + modules = utils.returnRigModules() + validName = False + msg = "A module with that name already exists. Please enter a unique name for the module" + + for module in modules: + name = cmds.getAttr(module + ".moduleName") + if name == str(self.previewName.text()): + cmds.confirmDialog(title="Name Exists", message=msg, icon="critical") + return + + # update groupbox label + originalName = self.modInst.groupBox.title() + self.modInst.groupBox.setTitle(str(self.previewName.text())) + + # update network node moduleName attribute + networkNode = self.modInst.returnNetworkNode + cmds.setAttr(networkNode + ".moduleName", lock=False) + cmds.setAttr(networkNode + ".moduleName", str(self.previewName.text()), type="string", lock=True) + + # update self.name for rig module + self.modInst.name = str(self.previewName.text()) + + # update created bones attribute values + prefix = str(self.prefix.text()) + suffix = str(self.suffix.text()) + + if len(prefix) > 0: + prefix += prefix + "_" + if len(suffix) > 0: + suffix = "_" + suffix + + createdBones = self.modInst.returnCreatedJoints + + attrString = "" + + for bone in createdBones: + niceName = bone + if len(bone) > 1: + if self.prefixInc != "": + niceName = bone.partition(self.prefixInc)[2] + if self.suffixInc != "": + niceName = niceName.partition(self.suffixInc)[0] + + attrString += prefix + niceName + suffix + "::" + + cmds.setAttr(networkNode + ".Created_Bones", lock=False) + cmds.setAttr(networkNode + ".Created_Bones", attrString, type="string", lock=True) + + # joint mover renaming + cmds.select(originalName + "_mover_grp", hi=True) + jointMoverNodes = cmds.ls(sl=True, type="transform") + constraints = ["pointConstraint", "orientConstraint", "parentConstraint"] + + for node in jointMoverNodes: + try: + if cmds.nodeType(node) not in constraints: + locked = cmds.lockNode(node, q=True) + if locked: + cmds.lockNode(node, lock=False) + nodeName = node.partition(originalName)[2] + newName = self.modInst.name + nodeName + cmds.rename(node, newName) + + if locked: + cmds.lockNode(node, lock=True) + except Exception, e: + pass + + # update outliner names + utils.findAndRenameOutlinerChildren(self.modInst.outlinerWidgets[self.modInst.originalName + "_treeModule"], + originalName, self.modInst.name) + self.modInst.outlinerWidgets[self.modInst.originalName + "_treeModule"].setText(0, self.modInst.name) + + # find module's selection scriptJob and delete it + jobs = cmds.scriptJob(lj=True) + for job in jobs: + compareString = job.partition(":")[0] + if str(self.modInst.scriptJob) == str(compareString): + cmds.scriptJob(kill=self.modInst.scriptJob) + break + + # replace module's outliner control entries with the new control name + for item in self.modInst.outlinerControls: + item[1] = item[1].replace(originalName, self.modInst.name) + + # create the selection script job again + self.modInst.createScriptJob() + + # find any modules using any of the created joints from this module that match the OLD name + createdJoints = self.modInst.returnCreatedJoints + modules = self.modInst.getAllModules + + for mod in modules: + attrs = cmds.listAttr(mod) + if "parentModuleBone" in attrs: + value = cmds.getAttr(mod + ".parentModuleBone") + if value in createdBones: + index = createdBones.index(value) + + # update those modules' network node parentModuleBone attribute + cmds.setAttr(mod + ".parentModuleBone", lock=False) + cmds.setAttr(mod + ".parentModuleBone", createdJoints[index], type="string", lock=True) + + # and also those modules' skeletonSettingsUI currentParent label + modName = cmds.getAttr(mod + ".moduleName") + + for each in self.rigUiInst.moduleInstances: + if each.networkNode == mod: + # find the current groupBox for this module + for i in range(self.rigUiInst.moduleSettingsLayout.count()): + if type(self.rigUiInst.moduleSettingsLayout.itemAt(i).widget()) == QtWidgets.QGroupBox: + if self.rigUiInst.moduleSettingsLayout.itemAt(i).widget().title() == modName: + self.rigUiInst.moduleSettingsLayout.itemAt(i).widget().setParent(None) + + # relaunch the skeleton settings UI with new info + each.skeletonSettings_UI(modName) + + # update mirrorModule field + for mod in modules: + attrs = cmds.listAttr(mod) + if "mirrorModule" in attrs: + value = cmds.getAttr(mod + ".mirrorModule") + + if value == originalName: + cmds.setAttr(mod + ".mirrorModule", lock=False) + cmds.setAttr(mod + ".mirrorModule", self.modInst.name, type="string", lock=True) + + # and also those modules' skeletonSettingsUI currentParent label + modName = cmds.getAttr(mod + ".moduleName") + + for each in self.rigUiInst.moduleInstances: + if each.networkNode == mod: + # find the current groupBox for this module + for i in range(self.rigUiInst.moduleSettingsLayout.count()): + if type(self.rigUiInst.moduleSettingsLayout.itemAt(i).widget()) == QtWidgets.QGroupBox: + if self.rigUiInst.moduleSettingsLayout.itemAt(i).widget().title() == modName: + self.rigUiInst.moduleSettingsLayout.itemAt(i).widget().setParent(None) + + # relaunch the skeleton settings UI with new info + each.skeletonSettings_UI(modName) + + # delete the UI + mayaWindow = interfaceUtils.getMainWindow() + mayaWindow = mayaWindow.objectName() + cmds.deleteUI(mayaWindow + "|" + windowObject) + + cmds.select(clear=True) diff --git a/Core/Scripts/Interfaces/ART_ChangeModuleParentUI.py b/Core/Scripts/Interfaces/ART_ChangeModuleParentUI.py new file mode 100644 index 0000000..958c175 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ChangeModuleParentUI.py @@ -0,0 +1,244 @@ +""" +Author: Jeremy Ernst +""" +import maya.cmds as cmds + +import System.interfaceUtils as interfaceUtils +import System.riggingUtils as riggingUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + +# maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + + +def getMainWindow(): + """ + Returns a pointer to Maya's window as a QWidget. + + """ + + import maya.OpenMayaUI as mui + pointer = mui.MQtUtil.mainWindow() + # pyside QMainWindow takes in a QWidget rather than QObject + return shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + + +windowTitle = "Change Module Parent" +windowObject = "pyArtChangeModuleParentUi" + + +class ART_ChangeModuleParent_UI(QtWidgets.QMainWindow): + """ + This class allows the user to change the parent module bone of a given module. It is found within the + skeletonSettingsUI of an individual module in the Rig Creator. + + .. image:: /images/changeModParent.png + + """ + + def __init__(self, currentParent, moduleInst, rigUiInst, parent=None): + """ + Instantiates the class, taking in current module information, and builds the interface. + + :param currentParent: The current module parent bone of this module. + :param moduleInst: The instance in memory of the module whose name is to change. + :param rigUiInst: The instance in memory of the Rig Creator UI from which this class was called. + + """ + super(ART_ChangeModuleParent_UI, self).__init__(parent) + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + # create class variables + self.currentParent = currentParent + self.modInst = moduleInst + self.rigUiInst = rigUiInst + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + style = f.read() + f.close() + + self.setStyleSheet(style) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + # set qt object name + self.setObjectName(windowObject) + self.setWindowTitle(windowTitle) + + # create the mainLayout for the rig creator UI + self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) + self.mainLayout.setContentsMargins(0, 0, 0, 0) + + self.setSizePolicy(mainSizePolicy) + self.setMinimumSize(QtCore.QSize(250, 400)) + self.setMaximumSize(QtCore.QSize(250, 400)) + + # create the background image + self.frame = QtWidgets.QFrame() + self.mainLayout.addWidget(self.frame) + self.frame.setObjectName("mid") + + # create the layout for the widgets + self.widgetLayout = QtWidgets.QVBoxLayout(self.frame) + + label = QtWidgets.QLabel("Choose New Parent:") + font = QtGui.QFont() + font.setBold(True) + label.setFont(font) + self.widgetLayout.addWidget(label) + + self.boneSearch = QtWidgets.QLineEdit() + self.boneSearch.setPlaceholderText("Search..") + self.boneSearch.textChanged.connect(self.searchList) + self.widgetLayout.addWidget(self.boneSearch) + self.boneList = QtWidgets.QListWidget() + self.widgetLayout.addWidget(self.boneList) + self.boneList.setMinimumHeight(200) + + # add items to comboBox + bones = utils.getViableParents() + + # get our own bones + modBones = self.modInst.returnCreatedJoints + + for bone in bones: + if bone not in modBones: + self.boneList.addItem(bone) + if bone == "root": + index = bones.index(bone) + self.boneList.setCurrentRow(index) + + # update button + self.updateBtn = QtWidgets.QPushButton("UPDATE") + self.widgetLayout.addWidget(self.updateBtn) + self.updateBtn.setMinimumSize(QtCore.QSize(230, 40)) + self.updateBtn.setMaximumSize(QtCore.QSize(230, 40)) + self.updateBtn.setSizePolicy(mainSizePolicy) + font = QtGui.QFont() + font.setPointSize(12) + self.updateBtn.setFont(font) + self.updateBtn.setObjectName("blueButton") + + # hookup signal/slot on create button + self.updateBtn.clicked.connect(self.applyModuleParentChange) + + self.updateBtn.setFocus() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def searchList(self): + """ + Reads the text in the QLineEdit and searches the list widget for any items containing the search text, + hiding all listWidgetItems that do not contain the search text. + + """ + + searchText = self.boneSearch.text() + + for i in range(self.boneList.count()): + lwItem = self.boneList.item(i) + if lwItem.text().find(searchText) != -1: + lwItem.setHidden(False) + else: + lwItem.setHidden(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def applyModuleParentChange(self): + """ + Gets the new parent from the selected ListWidgetItem text and then checks to make sure the selected parent + isn't a bone that is part of the module we're trying to change the parent on. Then updates text and attribute + values where needed. + + .. note:: + The following things get updated: + * Current Parent text item in the Skeleton Settings UI + * Network Node .parentModuleBone attribute + * Constrains nodes based on new parenting relationship + + """ + + # get new parent + newParent = self.boneList.currentItem().text() + + # check to make sure new parent is not in this module's created bones list + createdBones = self.modInst.returnCreatedJoints + + if newParent in createdBones: + cmds.confirmDialog(title="Error", icon="critical", + message="Cannot parent a module to a bone created by the module.") + return + + # update current parent text + self.modInst.currentParent.setText(newParent) + + # update network node parentModuleBone attribute + networkNode = self.modInst.returnNetworkNode + cmds.setAttr(networkNode + ".parentModuleBone", lock=False) + cmds.setAttr(networkNode + ".parentModuleBone", newParent, type="string", lock=True) + + # delete the existing bone connection and reparent to the new parent and recreate the bone connection + if cmds.objExists(self.modInst.name + "_parentGrp"): + cmds.delete(self.modInst.name + "_parentGrp") + + # parent under the new parent + moverGrp = cmds.getAttr(networkNode + ".moduleName") + moverGrp = moverGrp + "_mover_grp" + + if newParent == "root": + mover = "root_mover" + offsetMover = "root_mover" + + else: + networkNodes = utils.returnRigModules() + mover = utils.findMoverNodeFromJointName(networkNodes, newParent, False, True) + offsetMover = utils.findMoverNodeFromJointName(networkNodes, newParent) + + # create the new bone representation + childMover = utils.findOffsetMoverFromName(self.modInst.name) + riggingUtils.createBoneConnection(offsetMover, childMover, self.modInst.name) + + # delete the old constraint and create the new one + if cmds.objExists(self.modInst.name + "_mover_grp_parentConstraint*"): + cmds.delete(self.modInst.name + "_mover_grp_parentConstraint*") + + networkNodes = utils.returnRigModules() + mover = utils.findMoverNodeFromJointName(networkNodes, newParent, False, True) + if mover is not None: + cmds.parentConstraint(mover, self.modInst.name + "_mover_grp", mo=True) + + if cmds.objExists(self.modInst.name + "_mover_grp_scaleConstraint*"): + cmds.delete(self.modInst.name + "_mover_grp_scaleConstraint*") + + if mover is not None: + cmds.scaleConstraint(mover, self.modInst.name + "_mover_grp", mo=True) + + # delete the UI + mayaWindow = interfaceUtils.getMainWindow() + mayaWindow = mayaWindow.objectName() + cmds.deleteUI(mayaWindow + "|" + windowObject) + + cmds.select(clear=True) diff --git a/Core/Scripts/Interfaces/ART_DebugRigs.py b/Core/Scripts/Interfaces/ART_DebugRigs.py new file mode 100644 index 0000000..788f27e --- /dev/null +++ b/Core/Scripts/Interfaces/ART_DebugRigs.py @@ -0,0 +1,202 @@ +""" +Author: Jeremy Ernst +""" + +import os + +from functools import partial + +import System.riggingUtils as riggingUtils +import System.utils as utils +import maya.cmds as cmds +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_DebugRigs(): + """ + This class is used in developing rigs for modules and quickly testing them without having to go + through the entire build/publish process. + + .. image:: /images/debugRigs.png + + """ + + def __init__(self, mainUI): + """ + Instantiate the class, getting the QSettings, and building the interface. + + :param mainUI: The instance of the Rig Creator UI from which this class was called. + + """ + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.projectPath = settings.value("projectPath") + + self.mainUI = mainUI + + # images + self.imageBkgrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/toolbar_background.png")) + self.imageBtnBkrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/blue_field_background.png")) + self.frameBackground = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/field_background.png")) + + # build the UI + if cmds.window("ART_DebugRigsWin", exists=True): + cmds.deleteUI("ART_DebugRigsWin", wnd=True) + + self.buildUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + """ + Build the UI, listing all modules in the scene that make up the asset for the user to select and build rigs + for the selected. + + """ + + # create the main window + self.mainWin = QtWidgets.QMainWindow(self.mainUI) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.mainWin.setStyleSheet(self.style) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + # set qt object name + self.mainWin.setObjectName("ART_DebugRigsWin") + self.mainWin.setWindowTitle("Build Rigs") + + # font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + # set size policy + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + self.mainWin.resize(400, 300) + self.mainWin.setSizePolicy(mainSizePolicy) + self.mainWin.setMinimumSize(QtCore.QSize(400, 300)) + self.mainWin.setMaximumSize(QtCore.QSize(400, 300)) + + # create the QFrame for this page + self.background = QtWidgets.QFrame() + self.background.setObjectName("mid") + self.layout.addWidget(self.background) + self.mainLayout = QtWidgets.QHBoxLayout(self.background) + + # create the list on the left and add the modules to the list + self.moduleList = QtWidgets.QListWidget() + self.mainLayout.addWidget(self.moduleList) + + for mod in self.mainUI.moduleInstances: + item = QtWidgets.QListWidgetItem(mod.name) + item.setData(QtCore.Qt.UserRole, mod) + self.moduleList.addItem(item) + + # create our buttons on the right + self.rightLayout = QtWidgets.QVBoxLayout() + self.mainLayout.addLayout(self.rightLayout) + + infoText = "This tool is only for testing rigs in development. " + infoText += "It will leave behind nodes in your scene that you do NOT want to publish with. " + infoText += "When using this tool, it is advised to open a clean scene to publish your final asset." + + self.info = QtWidgets.QLabel() + self.rightLayout.addWidget(self.info) + self.info.setWordWrap(True) + self.info.setMinimumSize(150, 125) + self.info.setMaximumSize(150, 125) + self.info.setText(infoText) + + self.rightLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 200, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + self.buildButton = QtWidgets.QPushButton("Build Rigs For Selected") + self.buildButton.setObjectName("blueButton") + self.rightLayout.addWidget(self.buildButton) + self.buildButton.setMinimumSize(150, 40) + self.buildButton.setMaximumSize(150, 40) + self.buildButton.clicked.connect(partial(self.buildRigs)) + + self.deleteButton = QtWidgets.QPushButton("Remove Selected Rigs") + self.deleteButton.setObjectName("blueButton") + self.rightLayout.addWidget(self.deleteButton) + self.deleteButton.setMinimumSize(150, 40) + self.deleteButton.setMaximumSize(150, 40) + self.deleteButton.clicked.connect(partial(self.deleteRig)) + + self.closeButton = QtWidgets.QPushButton("Close") + self.closeButton.setObjectName("blueButton") + self.rightLayout.addWidget(self.closeButton) + self.closeButton.setMinimumSize(150, 40) + self.closeButton.setMaximumSize(150, 40) + self.closeButton.clicked.connect(partial(self.close)) + + self.mainWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildRigs(self): + """ + Builds the rigs for the selected module by calling on that module's buildRig function. + + """ + + data = self.moduleList.currentItem().data(QtCore.Qt.UserRole) + + # call on inst build rigs functions + if not cmds.objExists("driver_root"): + riggingUtils.createDriverSkeleton() + data.buildRig(None, self.mainUI) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def deleteRig(self): + """ + Deletes the rigs for the selected module by calling on that module's deleteRig function. + + """ + + data = self.moduleList.currentItem().data(QtCore.Qt.UserRole) + data.deleteRig() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def close(self): + """ + Close the interface and delete the window. + + """ + + if cmds.window("ART_DebugRigsWin", exists=True): + cmds.deleteUI("ART_DebugRigsWin", wnd=True) diff --git a/Core/Scripts/Interfaces/ART_EditRigUI.py b/Core/Scripts/Interfaces/ART_EditRigUI.py new file mode 100644 index 0000000..6bcfa3f --- /dev/null +++ b/Core/Scripts/Interfaces/ART_EditRigUI.py @@ -0,0 +1,570 @@ +# coding=utf-8 +""" +Author: Jeremy Ernst +""" + +import os +from functools import partial + +import System.interfaceUtils as interfaceUtils +import System.utils as utils +import maya.cmds as cmds +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + +# maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + + +def getMainWindow(): + + """ + Get Maya’s window as a QWidget and return the item in memory. + + :return: a QWidget of Maya’s window + + """ + + import maya.OpenMayaUI as mui + pointer = mui.MQtUtil.mainWindow() + # pyside QMainWindow takes in a QWidget rather than QObject + return shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + + +windowTitle = "Edit Rig" +windowObject = "pyArtEditRigWin" + + +class ART_EditRigUI(QtWidgets.QMainWindow): + """ + This class builds a tool that allows a user to Edit a Rig or Add Character for Animation. Both functions use the + same interface. The title and button text get swapped out depending on which situation has been called for by + the A.R.T.2.0 menu. + + .. image:: /images/editRig.png + + """ + + def __init__(self, edit, add, parent=None): + """ + Instantiates the class, getting the QSettings and building the interface. + + :param edit: Whether or not the operation is to edit the rig. + :param add: Whether or not the operation is to add the character for animation. + + """ + + super(ART_EditRigUI, self).__init__(parent) + self.edit = edit + self.add = add + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + # build the UI + self.createUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createUI(self): + """ + Builds the UI, listing options for choosing a project and showing all assets belonging to that project for + edit or add. + + """ + + # fonts + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + fontSmall = QtGui.QFont() + fontSmall.setPointSize(9) + fontSmall.setBold(True) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.setStyleSheet(self.style) + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # ======================================================================= + # #create the main widget + # ======================================================================= + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + # set qt object name + self.setObjectName(windowObject) + if self.edit: + self.setWindowTitle(windowTitle) + if self.add: + self.setWindowTitle("Add Rig For Animation") + + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) + + # create the mainLayout + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + self.resize(400, 400) + self.setSizePolicy(mainSizePolicy) + self.setMinimumSize(QtCore.QSize(400, 400)) + self.setMaximumSize(QtCore.QSize(400, 400)) + + # create the QFrame + self.frame = QtWidgets.QFrame() + self.layout.addWidget(self.frame) + self.widgetLayout = QtWidgets.QHBoxLayout(self.frame) + self.frame.setObjectName("mid") + + # ======================================================================= + # #create two VBox Layouts to create 2 columns + # ======================================================================= + self.leftColumn = QtWidgets.QVBoxLayout() + self.widgetLayout.addLayout(self.leftColumn) + + self.rightColumn = QtWidgets.QVBoxLayout() + self.widgetLayout.addLayout(self.rightColumn) + + # ======================================================================= + # #left column : project comboBox, group comboBox, listWidget of characters + # ======================================================================= + + self.projectMenu = QtWidgets.QComboBox() + self.leftColumn.addWidget(self.projectMenu) + self.projectMenu.setMinimumSize(150, 30) + self.projectMenu.setMaximumSize(150, 30) + self.projectMenu.currentIndexChanged.connect(self.populateGroups) + + self.groupMenu = QtWidgets.QComboBox() + self.leftColumn.addWidget(self.groupMenu) + self.groupMenu.setMinimumSize(150, 30) + self.groupMenu.setMaximumSize(150, 30) + self.groupMenu.currentIndexChanged.connect(self.populateCharacters) + + self.characterList = QtWidgets.QListWidget() + self.leftColumn.addWidget(self.characterList) + self.characterList.setMinimumSize(150, 200) + self.characterList.setMaximumSize(150, 200) + self.characterList.itemClicked.connect(partial(self.populateIcon)) + self.populateProjects() + # ======================================================================= + # #right column: icon frame, edit button/add button, close button + # ======================================================================= + + self.characterIcon = QtWidgets.QLabel() + self.characterIcon.setMinimumSize(200, 200) + self.characterIcon.setMaximumSize(200, 200) + self.rightColumn.addWidget(self.characterIcon) + + # default image + self.defaultPixMap = QtGui.QPixmap(utils.returnNicePath(self.iconsPath, "System/noCharacter.png")) + self.characterIcon.setPixmap(self.defaultPixMap) + + # if edit: + if self.edit: + self.editButton = QtWidgets.QPushButton("Edit Selected") + self.editButton.setFont(font) + self.rightColumn.addWidget(self.editButton) + self.editButton.setMinimumSize(200, 40) + self.editButton.setMaximumSize(200, 40) + self.editButton.clicked.connect(partial(self.editSelected)) + self.editButton.setObjectName("blueButton") + + # if add: + if self.add: + self.addButton = QtWidgets.QPushButton("Add Selected") + self.addButton.setFont(font) + self.rightColumn.addWidget(self.addButton) + self.addButton.setMinimumSize(200, 40) + self.addButton.setMaximumSize(200, 40) + self.addButton.clicked.connect(partial(self.addSelected)) + self.addButton.setObjectName("blueButton") + + self.closeButton = QtWidgets.QPushButton("Close") + self.closeButton.setFont(font) + self.rightColumn.addWidget(self.closeButton) + self.closeButton.setMinimumSize(200, 40) + self.closeButton.setMaximumSize(200, 40) + self.closeButton.clicked.connect(partial(self.closeUI)) + self.closeButton.setObjectName("blueButton") + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateProjects(self): + """ + Find all projects on disk (using the project path setting) and add each project to the QComboBox. Then, + call on populateGroups. + + .. seealso:: ART_EditRigUI.populateGroups() + + """ + + # if the project path doesn't exist on disk, create it + if not os.path.exists(self.projectPath): + os.makedirs(self.projectPath) + + # get a list of the existing folders in projects + existingProjects = os.listdir(self.projectPath) + folders = [] + + # find out which returned items are directories + for each in existingProjects: + if os.path.isdir(os.path.join(self.projectPath, each)): + folders.append(each) + + # add each project to the combo box + self.projectMenu.clear() + for each in folders: + self.projectMenu.addItem(each) + + # find selected project and populate groups + self.populateGroups() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateGroups(self): + """ + Given the selected project, populate any groups for that project (using the project path from QSettings) and + add them to the groups QComboBox. + + Then, call on populateCharacters. + + .. seealso:: ART_EditRigUI.populateCharacters() + + """ + + # get a list of the existing folders in projects + selectedProject = self.projectMenu.currentText() + project = os.path.join(self.projectPath, selectedProject) + existingGroups = os.listdir(project) + folders = [] + + # find out which returned items are directories + for each in existingGroups: + if os.path.isdir(os.path.join(project, each)): + folders.append(each) + + # otherwise, add each project to the combo box + self.groupMenu.clear() + self.groupMenu.addItem(" ") + for each in folders: + self.groupMenu.addItem(each) + + # populate characters + self.populateCharacters() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateCharacters(self): + """ + Given the selected project and group, populate the QListWidget with any assets found using that information. + The project path comes from the QSettings, the group is a subfolder of the project. + + """ + + # add project button + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + # get a list of the existing folders in projects + selectedProject = self.projectMenu.currentText() + fullPath = os.path.join(self.projectPath, selectedProject) + selectedGroup = self.groupMenu.currentText() + if len(selectedGroup) > 1: + fullPath = os.path.join(fullPath, selectedGroup) + + existingCharacters = os.listdir(fullPath) + files = [] + + # find out which returned items are directories + for each in existingCharacters: + if os.path.isfile(os.path.join(fullPath, each)): + if each.rpartition(".")[2] == "ma": + files.append(each) + + # otherwise, add each project to the combo box + self.characterList.clear() + + for each in files: + item = QtWidgets.QListWidgetItem(each.partition(".ma")[0]) + item.setFont(font) + self.characterList.addItem(item) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateIcon(self, *args): + """ + Given the selected character, display the correct icon for that character in the UI. + + """ + + # default + self.characterIcon.setPixmap(self.defaultPixMap) + + # get a list of the existing folders in projects + selectedProject = self.projectMenu.currentText() + fullPath = utils.returnNicePath(self.projectPath, selectedProject) + selectedGroup = self.groupMenu.currentText() + if len(selectedGroup) > 1: + fullPath = utils.returnNicePath(fullPath, selectedGroup) + + selectedCharacter = self.characterList.currentItem().text() + fullPath = utils.returnNicePath(fullPath, selectedCharacter + ".png") + + if os.path.exists(fullPath): + pixmap = QtGui.QPixmap(fullPath) + self.characterIcon.setPixmap(pixmap) + + else: + self.characterIcon.setPixmap(self.defaultPixMap) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def editSelected(self, *args): + """ + Find the selected character, and open that file for edit. The path is constructed using the QSettings info, + with any group as a subfolder, and lastly the selected asset/character as the last part of the path. + + """ + + # get a list of the existing folders in projects + selectedProject = self.projectMenu.currentText() + fullPath = utils.returnNicePath(self.projectPath, selectedProject) + selectedGroup = self.groupMenu.currentText() + if len(selectedGroup) > 1: + fullPath = utils.returnNicePath(fullPath, selectedGroup) + + selectedCharacter = self.characterList.currentItem().text() + mayaFile = utils.returnNicePath(fullPath, selectedCharacter + ".ma") + iconFile = utils.returnNicePath(fullPath, selectedCharacter + ".png") + + if os.path.exists(mayaFile): + if os.path.exists(iconFile): + launchUI = False + + # get current file + currentFile = cmds.file(q=True, sceneName=True) + if cmds.file(currentFile, q=True, modified=True) == True: + proceed = self.unsavedChanges(currentFile) + + if proceed == 0: + cmds.file(save=True, force=True) + cmds.file(mayaFile, open=True, prompt=True, options="v=0", ignoreVersion=True, typ="mayaAscii", + f=True) + launchUI = True + + if proceed == 1: + cmds.file(mayaFile, open=True, prompt=True, options="v=0", ignoreVersion=True, typ="mayaAscii", + f=True) + launchUI = True + + if proceed == 2: + return + else: + cmds.file(mayaFile, open=True, prompt=True, options="v=0", ignoreVersion=True, typ="mayaAscii", + f=True) + launchUI = True + + if launchUI: + import Interfaces.ART_RigCreatorUI as ART_RigCreatorUI + reload(ART_RigCreatorUI) + ART_RigCreatorUI.createUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addSelected(self, *args): + """ + Finds the selected character, and references that file into the currently opened scene. The path is + constructed using the QSettings info, with any group as a subfolder, and lastly the selected asset/character + as the last part of the path. + + Also handles namespaces, adding the new namespace to the network node of the added asset, and launches the + animationUI. + + """ + + # get a list of the existing folders in projects + selectedProject = self.projectMenu.currentText() + fullPath = utils.returnNicePath(self.projectPath, selectedProject) + selectedGroup = self.groupMenu.currentText() + if len(selectedGroup) > 1: + fullPath = utils.returnNicePath(fullPath, selectedGroup) + + selectedCharacter = self.characterList.currentItem().text() + mayaFile = utils.returnNicePath(fullPath, selectedCharacter + ".ma") + iconFile = utils.returnNicePath(fullPath, selectedCharacter + ".png") + + # reference in the selected character if the file exists on drive + if os.path.exists(mayaFile): + if os.path.exists(iconFile): + + # find existing namespaces in scene + namespaces = cmds.namespaceInfo(listOnlyNamespaces=True) + + # reference the rig file + cmds.file(mayaFile, r=True, type="mayaAscii", loadReferenceDepth="all", namespace=selectedCharacter, + options="v=0") + + # clear selection and fit view + cmds.select(clear=True) + cmds.viewFit() + panels = cmds.getPanel(type='modelPanel') + + # turn on smooth shading + for panel in panels: + editor = cmds.modelPanel(panel, q=True, modelEditor=True) + cmds.modelEditor(editor, edit=True, displayAppearance="smoothShaded", displayTextures=True, + textures=True) + + # find new namespaces in scene to figure out the namespace that was created upon referencing the + # character + newCharacterName = selectedCharacter + newNamespaces = cmds.namespaceInfo(listOnlyNamespaces=True) + + for name in newNamespaces: + if name not in namespaces: + newCharacterName = name + + # add an attribute to the rig root (if needed) and set the namespace attr to the newCharacterName + if cmds.objExists(newCharacterName + ":ART_RIG_ROOT"): + if not cmds.objExists(newCharacterName + ":ART_RIG_ROOT.namespace"): + cmds.addAttr(newCharacterName + ":ART_RIG_ROOT", ln="namespace", dt="string", keyable=False) + + cmds.setAttr(newCharacterName + ":ART_RIG_ROOT.namespace", newCharacterName, type="string") + + # delete any interfaces that may be up + self.closeUI() + + if cmds.dockControl("pyArtRigCreatorDock", q=True, exists=True): + if cmds.window("pyArtRigCreatorUi", exists=True): + cmds.deleteUI("pyArtRigCreatorUi", wnd=True) + cmds.deleteUI("pyArtRigCreatorDock", control=True) + + # launch anim UI + cmds.refresh(force=True) + + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir, sheet)) + + import Interfaces.ART_AnimationUI as ART_AnimationUI + ART_AnimationUI.run() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def unsavedChanges(self, path): + """ + Displays a message box that warns the user of unsaved file changed and returns their response. + + :return: Returns the user response (Save, Don't Save, Cancel) + + """ + + # message box for letting user know current file has unsaved changes + msgBox = QtWidgets.QMessageBox() + msgBox.setIcon(QtWidgets.QMessageBox.Warning) + msgBox.setText("Current File Has Unsaved Changes!") + msgBox.addButton("Save Changes", QtWidgets.QMessageBox.YesRole) + msgBox.addButton("Don't Save", QtWidgets.QMessageBox.NoRole) + msgBox.addButton("Cancel", QtWidgets.QMessageBox.NoRole) + ret = msgBox.exec_() + + return ret + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeUI(self): + """ + Closes and deletes the interface for the class. + + """ + + if cmds.window("pyArtEditRigWin", exists=True): + cmds.deleteUI("pyArtEditRigWin", wnd=True) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +def run(): + """ + Creates an instance of the class for Editing a rig. The ARTv2 menu calls on this function. + + """ + + if cmds.window("pyArtEditRigWin", exists=True): + cmds.deleteUI("pyArtEditRigWin", wnd=True) + + gui = ART_EditRigUI(True, False, getMainWindow()) + gui.show() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +def runAdd(): + """ + Creates an instance of the class for adding a rig for animation. The artv2 menu calls this function. + + """ + + if cmds.window("pyArtEditRigWin", exists=True): + cmds.deleteUI("pyArtEditRigWin", wnd=True) + + gui = ART_EditRigUI(False, True, getMainWindow()) + gui.show() diff --git a/Core/Scripts/Interfaces/ART_ExportMeshes.py b/Core/Scripts/Interfaces/ART_ExportMeshes.py new file mode 100644 index 0000000..8bc3d8e --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ExportMeshes.py @@ -0,0 +1,1733 @@ +""" +Author: Jeremy Ernst +""" + +import json +import os +from functools import partial + +import System.utils as utils +import maya.cmds as cmds +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_ExportMeshes(QtWidgets.QMainWindow): + """ + This class is used to export skeletal meshes and skeletal mesh LODs. + + The UI has a robust suite of tools for managing LODs, bone removal for LODs, choosing + which meshes are associated with a LOD, where weighting from removed bones will get transferred, + and setting/viewing LOD poses (useful if you wanted to remove finger bones for a LOD, but not have + "paddle hands" + + .. image:: /images/exportMeshes.png + + A look at the LOD tools for transferring weighting and managing LOD poses: + + .. image:: /images/lodTool.png + + """ + + def __init__(self, mainUI, parent=None): + """ + Instantiates the class, getting the QSettings, presenting a QMessageBox about saving the current file, + creates a temporary file to do the export work out of (stripping out the rig and removing all connections + from joints), set the model pose, then calls on the UI build. + + :param mainUI: Instance of the Rig Creator interface, from which this class was called. + + .. seealso:: ART_ExportMeshes.buildUI(), ART_ExportMeshes.populateUI() + + """ + + super(ART_ExportMeshes, self).__init__(parent) + + if cmds.window("pyART_ExportMeshesWin", exists=True): + cmds.deleteUI("pyART_ExportMeshesWin", wnd=True) + + # get settings + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.scriptPath = settings.value("scriptPath") + self.iconsPath = settings.value("iconPath") + self.projectPath = settings.value("projectPath") + + # class vars + self.mainUI = mainUI + self.lodPoseDict = {} + + # get current file + self.saveFile = cmds.file(q=True, sceneName=True) + + # message box for confirming save action + msgBax = QtWidgets.QMessageBox() + msgBax.setText( + "Please make sure any changes to the current file are saved before continuing. This process will be " + "creating a temporary file to do all of the exporting from.") + msgBax.setIcon(QtWidgets.QMessageBox.Warning) + msgBax.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + msgBax.setDefaultButton(QtWidgets.QMessageBox.Ok) + ret = msgBax.exec_() + + if ret == QtWidgets.QMessageBox.Ok: + + # save file as a new temporary file + self.tempFile = os.path.join(os.path.dirname(self.saveFile), "exportFile.ma") + cmds.file(rename=self.tempFile) + cmds.file(save=True) + + # remove all skeleton connections + cmds.select("root", hi=True) + selection = cmds.ls(sl=True) + for each in selection: + connT = cmds.connectionInfo(each + ".translate", sourceFromDestination=True) + if connT != '': + cmds.disconnectAttr(connT, each + ".translate") + connR = cmds.connectionInfo(each + ".rotate", sourceFromDestination=True) + if connR != '': + cmds.disconnectAttr(connR, each + ".rotate") + connS = cmds.connectionInfo(each + ".scale", sourceFromDestination=True) + if connS != '': + cmds.disconnectAttr(connS, each + ".scale") + + # remove rig and driver skeleton + cmds.select("rig_grp", hi=True) + selection = cmds.ls(sl=True) + + for each in selection: + cmds.lockNode(each, lock=False) + + cmds.delete("rig_grp") + cmds.delete("driver_root") + + # show all joints + cmds.select("root", hi=True) + joints = cmds.ls(sl=True) + for joint in joints: + cmds.setAttr(joint + ".v", lock=False) + cmds.setAttr(joint + ".v", 1) + + # go to model pose + for inst in self.mainUI.moduleInstances: + try: + # call on the module's bakeOffsets method + inst.setupForRigPose() + inst.setReferencePose("modelPose") + inst.cleanUpRigPose() + except Exception, e: + print e + + # build UI + self.buildUI() + + # populate UI + self.populateUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeWin(self, event): + """ + Gathers all information from the temp file (LOD meshes, bones to remove, etc), opens the export file, + and applies that information to the network node in the export file. Lastly removes the temp file. + + """ + + # save out data + exportData = utils.findExportMeshData() + + # open original file + cmds.file(self.saveFile, open=True, force=True) + + # apply data + characterNode = utils.returnCharacterModule() + lodData = ["_Pose", "_Bones", "_Meshes", "_FilePath"] + + for each in exportData: + # path, meshes, removeBones, poseData, lodAttr + for d in lodData: + + # if the attr does not exist, create it + if not cmds.objExists(characterNode + "." + each[4] + d): + if d != "_Meshes": + cmds.addAttr(characterNode, ln=each[4] + d, dt="string") + else: + cmds.addAttr(characterNode, ln=each[4] + d, at="message") + + # set the attr data + dataString = json.dumps(each[0]) + cmds.setAttr(characterNode + "." + each[4] + "_FilePath", dataString, type="string") + + dataString = json.dumps(each[2]) + cmds.setAttr(characterNode + "." + each[4] + "_Bones", dataString, type="string") + + dataString = json.dumps(each[3]) + cmds.setAttr(characterNode + "." + each[4] + "_Pose", dataString, type="string") + + # first remove all connections + i = each[4].partition("_")[2] + connections = cmds.listConnections(characterNode + "." + each[4] + "_Meshes") + if connections is not None: + for conn in connections: + cmds.disconnectAttr(characterNode + "." + each[4] + "_Meshes", conn + ".lodGroup" + str(i)) + + # Add attrs to meshes to connect up to the characterNode LOD meshes attr + for mesh in each[1]: + if not cmds.objExists(mesh + ".lodGroup" + str(i)): + cmds.addAttr(mesh, sn="lodGroup" + str(i), at="message") + + cmds.connectAttr(characterNode + "." + each[4] + "_Meshes", mesh + ".lodGroup" + str(i)) + + # remove temp file + os.remove(self.tempFile) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + """ + Builds the main interface for the tool, which allows additions of LODs, assignment of meshes to a given LOD, + and assignment of bones to remove per LOD (which then opens another interface/tool). + + .. seealso:: ART_ExportMeshes.addBoneToList_UI(), ART_ExportMeshes.addMeshToList_UI() + .. seealso:: ART_ExportMeshes.addMeshLOD(), ART_ExportMeshes.createLODpage() + .. seealso:: ART_ExportMeshes.createExportMeshesPage(), ART_ExportMeshes.removeLodTab() + .. seealso:: ART_ExportMeshes.export() + + Here is a breakdown image showing which UI elements call on which functions: + + .. image:: /images/exportMeshesBreakout.png + + """ + + self.closeEvent = self.closeWin + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.setStyleSheet(self.style) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWidget.setStyleSheet(self.style) + self.setCentralWidget(self.mainWidget) + + # set qt object name + self.setObjectName("pyART_ExportMeshesWin") + self.setWindowTitle("Export Skeletal Meshes") + + # font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + # set size policy + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + self.resize(600, 400) + self.setSizePolicy(mainSizePolicy) + self.setMinimumSize(QtCore.QSize(600, 400)) + self.setMaximumSize(QtCore.QSize(600, 400)) + + # build pages + self.createExportMeshesPage() + + # show window + self.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createExportMeshesPage(self): + """ + Creates the LOD0 Tab, which is a little bit unique from subsequent LOD tabs, as the Add/Remove LOD buttons + are added, and the LOD0 tab can not be removed. It still calls on createLODpage to create the common + elements, but this creates the framework for all of the LOD tabs. + + .. seealso:: ART_ExportMeshes.createLODpage() + + """ + + # create the QFrame for this page + self.exportMeshPage = QtWidgets.QFrame() + self.exportMeshPage.setStyleSheet(self.style) + self.exportMeshPage.setMinimumSize(560, 250) + self.layout.addWidget(self.exportMeshPage) + self.emPageMainLayout = QtWidgets.QVBoxLayout(self.exportMeshPage) + self.exportMeshPage.setStyleSheet(self.style) + self.exportMeshPage.setObjectName("dark") + + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + # add Tab Widget + # tab stylesheet (tab stylesheet via QSS doesn't seem to work for some reason + stylesheet = """ + QTabBar::tab + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(90,90,90), stop:1 rgb(30,30,30)); + border: 2px solid black; + width: 180px; + padding-left: -10px; + font: 8pt; + } + QTabBar::tab:selected + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(255,174,0), stop:1 rgb(30,30,30)); + border: 2px solid black; + font: bold 10pt; + } + QTabBar::tab:hover + { + background: rgb(132,95,16); + font: bold 10pt; + } + QTabBar::tab:!selected + { + margin-top: 5px; + } + QTabWidget::pane + { + border: 2px solid rgb(0,0,0); + } + """ + + self.emPageTabs = QtWidgets.QTabWidget() + self.emPageTabs.setStyleSheet(stylesheet) + self.emPageMainLayout.addWidget(self.emPageTabs) + + self.emPageTabBar = QtWidgets.QTabBar() + self.emPageTabs.setTabBar(self.emPageTabBar) + self.emPageTabBar.setContentsMargins(0, 0, 0, 0) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # LOD0 tab + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + self.createLODpage("LOD_0", False) + + cornerWidget = QtWidgets.QWidget() + cornerLayout = QtWidgets.QHBoxLayout(cornerWidget) + cornerLayout.setContentsMargins(0, 0, 0, 10) + + # remove lod button + self.removeMeshLodBtn = QtWidgets.QPushButton(" - ") + self.removeMeshLodBtn.setObjectName("blueButton") + self.removeMeshLodBtn.setMinimumHeight(20) + self.removeMeshLodBtn.setToolTip("Remove Mesh LOD (current tab)") + cornerLayout.addWidget(self.removeMeshLodBtn) + self.removeMeshLodBtn.clicked.connect(self.removeLodTab) + + # add lod button + self.addMeshLodBtn = QtWidgets.QPushButton(" + ") + self.addMeshLodBtn.setObjectName("blueButton") + self.addMeshLodBtn.setMinimumHeight(20) + self.addMeshLodBtn.setToolTip("Add Mesh LOD") + cornerLayout.addWidget(self.addMeshLodBtn) + self.addMeshLodBtn.clicked.connect(partial(self.addMeshLOD)) + + self.emPageTabs.setCornerWidget(cornerWidget) + + # add continue/back buttons + buttonlayout = QtWidgets.QHBoxLayout() + self.emPageMainLayout.addLayout(buttonlayout) + + spacerItem = QtWidgets.QSpacerItem(300, 0) + buttonlayout.addSpacerItem(spacerItem) + + self.emPage_continueButton = QtWidgets.QPushButton("Export") + buttonlayout.addWidget(self.emPage_continueButton) + self.emPage_continueButton.setFont(font) + self.emPage_continueButton.setMinimumSize(250, 50) + self.emPage_continueButton.setMaximumSize(250, 50) + self.emPage_continueButton.clicked.connect(partial(self.export)) + self.emPage_continueButton.setObjectName("blueButton") + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createLODpage(self, label, closeable): + """ + Creates a tab (QFrame) for a new LOD, with the UI elements to set the output path for the FBX, to set the + meshes assigned to the LOD, and to launch the bone removal tool for the LOD. + + :param label: The text label for the tab (LOD_#) + :param closeable: Whether this LOD can be removed, thus removing the tab. + + """ + + lodTab = QtWidgets.QFrame() + lodTab.setStyleSheet(self.style) + lodTab.setObjectName("mid") + + self.emPageTabs.addTab(lodTab, label) + + # horizontal layout + mainLayout = QtWidgets.QHBoxLayout(lodTab) + + # left side column + leftSide = QtWidgets.QVBoxLayout() + mainLayout.addLayout(leftSide) + + # label + meshLayout = QtWidgets.QHBoxLayout() + leftSide.addLayout(meshLayout) + layoutLabel = QtWidgets.QLabel("Meshes For " + label + ":") + meshLayout.addWidget(layoutLabel) + layoutLabel.setStyleSheet('background: transparent') + + chooseBtn = QtWidgets.QPushButton("Choose Meshes") + chooseBtn.setObjectName("blueButton") + meshLayout.addWidget(chooseBtn) + + # render mesh list + meshList = QtWidgets.QListWidget() + meshList.setToolTip("Select the meshes you want to include in this mesh LOD.") + meshList.setProperty("listType", "mesh") + leftSide.addWidget(meshList) + meshList.setToolTip("List of meshes to include for this LOD") + meshList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + + chooseBtn.clicked.connect(partial(self.addMeshToList_UI, self, label, meshList)) + + # right side column + rightLayout = QtWidgets.QVBoxLayout() + mainLayout.addLayout(rightLayout) + + # horizontal layout for line edit and button + pathLayout = QtWidgets.QHBoxLayout() + rightLayout.addLayout(pathLayout) + + lineEdit = QtWidgets.QLineEdit() + pathLayout.addWidget(lineEdit) + lineEdit.setMinimumHeight(35) + lineEdit.setMaximumHeight(35) + lineEdit.setPlaceholderText("FBX Export Path...") + lineEdit.setToolTip("Define the file name and file location for this LOD FBX file.") + lineEdit.textChanged.connect(partial(self.saveFilePath, lineEdit, label)) + + browseBtn = QtWidgets.QPushButton() + browseBtn.setMinimumSize(35, 35) + browseBtn.setMaximumSize(35, 35) + pathLayout.addWidget(browseBtn) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/fileBrowse.png")) + browseBtn.setIconSize(QtCore.QSize(30, 30)) + browseBtn.setIcon(icon) + browseBtn.clicked.connect(partial(self.browseToFBX, lineEdit)) + + # label + add button + removeBonesLayout = QtWidgets.QHBoxLayout() + rightLayout.addLayout(removeBonesLayout) + + layoutLabel = QtWidgets.QLabel("Bones to remove for " + label + ":") + removeBonesLayout.addWidget(layoutLabel) + layoutLabel.setStyleSheet('background: transparent;') + + removeBtn = QtWidgets.QPushButton("+") + removeBtn.setMinimumSize(35, 35) + removeBtn.setMaximumSize(35, 35) + removeBonesLayout.addWidget(removeBtn) + removeBtn.setObjectName("blueButton") + + # bone list + boneList = QtWidgets.QListWidget() + boneList.setProperty("listType", "bone") + rightLayout.addWidget(boneList) + boneList.setToolTip("List of joints to remove from this LOD") + boneList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + boneList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + + # signals/slots + removeBtn.clicked.connect(partial(self.addBoneToList_UI, self, label, boneList)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addBoneToList_UI(self, parent, label, listWidget): + """ + Creates an interface to remove bones from a LOD, transfer weighting of those removed bones to the next viable + parent, and handle LOD posing. + + :param parent: The instance of the main UI created by ART_ExportMeshes.buildUI() + :param label: The label for the window title to show what LOD this interface represents. + :param listWidget: The list widget on the main LOD page that lists all bones being removed. + + .. seealso:: ART_ExportMeshes.addBoneToList_Accept(), ART_ExportMeshes.addWeightingTransferEntry() + .. seealso:: ART_ExportMeshes.viewLodPose(), ART_ExportMeshes.resetLodPose(), ART_ExportMeshes.resetLodPose() + + """ + + # create the main window + mainWin = QtWidgets.QMainWindow(parent) + mainWin.setStyleSheet(self.style) + + # create the main widget + mainWidget = QtWidgets.QFrame() + mainWidget.setObjectName("mid") + mainWin.setCentralWidget(mainWidget) + mainWin.setMinimumSize(725, 525) + mainWin.setMaximumSize(725, 525) + + # set qt object name + mainWin.setObjectName("ART_AddBoneToLODlistWin") + mainWin.setWindowTitle(label) + + # font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + # create the mainLayout for the rig creator UI + layout = QtWidgets.QVBoxLayout(mainWidget) + mainLayout = QtWidgets.QHBoxLayout() + layout.addLayout(mainLayout) + + # treeWidget + self.tree = QtWidgets.QTreeWidget() + self.tree.setObjectName("light") + mainLayout.addWidget(self.tree) + self.tree.headerItem().setText(0, "") + self.tree.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.tree.setMinimumSize(QtCore.QSize(260, 470)) + self.tree.setMaximumSize(QtCore.QSize(260, 470)) + + self.tree.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.tree.header().setResizeMode(QtWidgets.QHeaderView.ResizeToContents) + self.tree.headerItem().setText(0, "Skeleton Tree") + self.tree.setColumnWidth(0, 260) + self.tree.setIndentation(10) + + # right side + rightLayout = QtWidgets.QVBoxLayout() + mainLayout.addLayout(rightLayout) + + # Weighting Transfer + xferGroupBox = QtWidgets.QGroupBox("Weighting Transfer") + rightLayout.addWidget(xferGroupBox) + xferGroupBox.setMinimumSize(QtCore.QSize(425, 330)) + xferGroupBox.setMaximumSize(QtCore.QSize(425, 330)) + xferGroupBox.setObjectName("light") + + # vboxLayout--hbox button layout for spacer/button, vboxlayout/scroll area for entries + weightingXferLayout = QtWidgets.QVBoxLayout(xferGroupBox) + rightLayout.addLayout(weightingXferLayout) + + buttonLayout = QtWidgets.QHBoxLayout() + weightingXferLayout.addLayout(buttonLayout) + buttonLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)) + + addEntryButton = QtWidgets.QPushButton(" Add Entry ") + addEntryButton.setObjectName("blueButton") + buttonLayout.addWidget(addEntryButton) + + # ScrollArea + scrollLayout = QtWidgets.QVBoxLayout() + weightingXferLayout.addLayout(scrollLayout) + + scrollArea = QtWidgets.QScrollArea() + scrollLayout.addWidget(scrollArea) + + scrollContents = QtWidgets.QFrame() + scrollContents.setObjectName("dark") + scrollArea.setWidget(scrollContents) + + entriesLayout = QtWidgets.QVBoxLayout(scrollContents) + scrollArea.setWidgetResizable(True) + + entriesLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + # LOD Pose + lodPoseGroupBox = QtWidgets.QGroupBox("LOD Posing") + rightLayout.addWidget(lodPoseGroupBox) + lodPoseGroupBox.setMinimumSize(QtCore.QSize(425, 75)) + lodPoseGroupBox.setMaximumSize(QtCore.QSize(425, 75)) + lodPoseGroupBox.setObjectName("light") + + lodPoseLayout = QtWidgets.QHBoxLayout(lodPoseGroupBox) + + viewPoseBtn = QtWidgets.QPushButton("View LOD Pose") + viewPoseBtn.setCheckable(True) + lodPoseLayout.addWidget(viewPoseBtn) + viewPoseBtn.clicked.connect(partial(self.viewLodPose, label, viewPoseBtn)) + + resetPoseBtn = QtWidgets.QPushButton("Reset LOD Pose") + lodPoseLayout.addWidget(resetPoseBtn) + resetPoseBtn.setObjectName("blueButton") + resetPoseBtn.clicked.connect(partial(self.resetLodPose, label)) + + savePoseBtn = QtWidgets.QPushButton("Save LOD Pose") + lodPoseLayout.addWidget(savePoseBtn) + savePoseBtn.setObjectName("blueButton") + savePoseBtn.clicked.connect(partial(self.saveLodPose, label)) + + # button + addButton = QtWidgets.QPushButton("Save and Close") + addButton.setMinimumHeight(40) + addButton.setMaximumHeight(40) + rightLayout.addWidget(addButton) + addButton.clicked.connect( + partial(self.addBoneToList_Accept, self.tree, listWidget, label, entriesLayout, viewPoseBtn)) + addButton.setObjectName("blueButton") + + addEntryButton.clicked.connect(partial(self.addWeightingTransferEntry, entriesLayout)) + + # joints + cmds.select("root", hi=True) + joints = cmds.ls(sl=True, type="joint") + + for joint in joints: + parent = cmds.listRelatives(joint, parent=True) + + if parent is None: + item = QtWidgets.QTreeWidgetItem(self.tree) + item.setText(0, joint) + item.setBackground(0, QtCore.Qt.NoBrush) + + children = cmds.listRelatives(joint, children=True, type="joint") + for child in children: + self.addBoneToListUI_addChildren(child, item) + + # show + self.tree.expandAll() + mainWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # populate ui automatically if the settings exist + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + characterNode = utils.returnCharacterModule() + attrs = cmds.listAttr(characterNode, ud=True, string="LOD_*_Bones") + lodAttrs = [] + + if attrs is not None: + for attr in attrs: + lodAttrs.append(attr.partition("_Bones")[0]) + + if lodAttrs is not None: + + # get json data from attribute + lodData = ["_Bones"] + boneValues = [] + + for attr in lodAttrs: + if attr == label: + for entry in lodData: + if cmds.objExists(characterNode + "." + attr + entry): + if entry == "_Bones": + boneValues = json.loads(cmds.getAttr(characterNode + "." + attr + entry)) + + for item in boneValues: + bones = item[1] + xferBone = item[0] + + # create an entry (need to return back the layouts needed for the next part + entryData = self.addWeightingTransferEntry(entriesLayout) + self.tree.clearSelection() + for each in bones: + item = self.tree.findItems(each, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0) + if item is not None: + item = item[0] + item.setSelected(True) + self.addItemsToWeightXferList(entryData[0], entryData[1]) + + self.tree.clearSelection() + item = self.tree.findItems(xferBone, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0) + if item is not None: + item = item[0] + item.setSelected(True) + self.addXferBoneToList(entryData[1], False) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addWeightingTransferEntry(self, layout): + """ + Adds a Groupbox with two QListWidgets, where the left shows bones to remove, and the right shows the next + viable parent bone to transfer the weighting to. Both lists can then be edited as well. + + This is what that would like like: + + .. image:: /images/xferEntry.png + + :param layout: The QVboxLayout to add the QGroupbox to. + + :return: returns both QListWidgets (in memory) + + .. seealso:: ART_ExportMeshes.addItemsToWeightXferList(), ART_ExportMeshes.removeBonesFromList() + + """ + + groupbox = QtWidgets.QGroupBox() + layout.insertWidget(0, groupbox) + groupbox.setMaximumSize(QtCore.QSize(370, 140)) + groupbox.setMaximumSize(QtCore.QSize(370, 140)) + groupbox.setCheckable(True) + groupbox.setFlat(True) + + # load style sheet file + styleSheetFile = utils.returnNicePath(self.toolsPath, + "Core/Scripts/Interfaces/StyleSheets/skeletonSettings.qss") + f = open(styleSheetFile, "r") + style = f.read() + f.close() + groupbox.setStyleSheet(style) + + mainLayout = QtWidgets.QHBoxLayout(groupbox) + + groupbox.toggled.connect(partial(self.collapseBox, groupbox)) + + removeBonesList = QtWidgets.QListWidget() + mainLayout.addWidget(removeBonesList) + removeBonesList.setMinimumSize(QtCore.QSize(150, 122)) + removeBonesList.setMaximumSize(QtCore.QSize(150, 122)) + removeBonesList.setProperty("list", "remove") + removeBonesList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + + removeBonesList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + removeBonesList.customContextMenuRequested.connect(partial(self.lodContextMenu, removeBonesList, groupbox)) + + buttonLayout = QtWidgets.QVBoxLayout() + mainLayout.addLayout(buttonLayout) + + addButton = QtWidgets.QPushButton(" + ") + buttonLayout.addWidget(addButton) + addButton.setObjectName("blueButton") + + buttonLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + remButton = QtWidgets.QPushButton(" - ") + buttonLayout.addWidget(remButton) + remButton.setObjectName("blueButton") + + xferBoneList = QtWidgets.QListWidget() + mainLayout.addWidget(xferBoneList) + xferBoneList.setMinimumSize(QtCore.QSize(120, 122)) + xferBoneList.setMaximumSize(QtCore.QSize(120, 122)) + xferBoneList.setProperty("list", "xfer") + + setButton = QtWidgets.QPushButton("<-") + mainLayout.addWidget(setButton) + setButton.setObjectName("blueButton") + setButton.clicked.connect(partial(self.addXferBoneToList, xferBoneList, False)) + + addButton.clicked.connect(partial(self.addItemsToWeightXferList, removeBonesList, xferBoneList)) + remButton.clicked.connect(partial(self.removeBonesFromList, removeBonesList)) + + return [removeBonesList, xferBoneList] + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def collapseBox(self, groupBox, *args): + """ + Collapses the given groupBox down to 16 pixel high, or restores its original height, given the state. + + :param groupBox: Which groupBox to operate on and manipulate the height. + :param args: What the state is of the groupBox checkBox. + + """ + + if args[0]: + groupBox.setMaximumSize(QtCore.QSize(370, 140)) + groupBox.setMinimumSize(QtCore.QSize(370, 140)) + else: + groupBox.setMaximumSize(QtCore.QSize(370, 16)) + groupBox.setMinimumSize(QtCore.QSize(370, 16)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addMeshLOD(self): + """ + Finds the current number of LOD tabs, constructs a label for the new tab, iterating the count by 1, + and calls on createLODpage, passing in that label. + + .. seealso:: ART_ExportMeshes.createLODpage() + + """ + + # get current count of tabs + numTabs = self.emPageTabs.count() + label = "LOD_" + str(numTabs) + + self.createLODpage(label, True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addMeshToList_UI(self, parent, label, listWidget): + """ + Creates a UI that lists all meshes for selection to assign meshes to a given LOD. + + .. image:: /images/addMeshesUI.png + + :param parent: The UI instance to parent this interface to + :param label: The LOD text label for this interface's window title. + :param listWidget: The listWidget on the main LOD page that will list the selected meshes + + .. image:: /images/meshList.png + + .. seealso:: ART_ExportMeshes.populateRenderMeshes(), ART_ExportMeshes.addMeshesToLodList() + + """ + + # create the main window + mainWin = QtWidgets.QMainWindow(parent) + mainWin.setStyleSheet(self.style) + + # create the main widget + mainWidget = QtWidgets.QFrame() + mainWidget.setObjectName("mid") + mainWin.setCentralWidget(mainWidget) + mainWin.setMinimumSize(300, 525) + mainWin.setMaximumSize(300, 525) + + # set qt object name + mainWin.setObjectName("ART_AddMeshesToLODlistWin") + mainWin.setWindowTitle(label) + + # font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + # create the mainLayout for the rig creator UI + layout = QtWidgets.QVBoxLayout(mainWidget) + mainLayout = QtWidgets.QHBoxLayout() + layout.addLayout(mainLayout) + + # treeWidget + meshTree = QtWidgets.QTreeWidget() + meshTree.setObjectName("light") + mainLayout.addWidget(meshTree) + meshTree.headerItem().setText(0, "") + meshTree.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + meshTree.setMinimumSize(QtCore.QSize(260, 470)) + meshTree.setMaximumSize(QtCore.QSize(260, 470)) + + meshTree.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + meshTree.header().setResizeMode(QtWidgets.QHeaderView.ResizeToContents) + meshTree.headerItem().setText(0, "Geometry") + meshTree.setColumnWidth(0, 260) + meshTree.setIndentation(10) + + chooseBtn = QtWidgets.QPushButton("Save and Close") + chooseBtn.setObjectName("blueButton") + layout.addWidget(chooseBtn) + chooseBtn.clicked.connect(partial(self.addMeshesToLodList, listWidget, meshTree, mainWin, label)) + + # populate tree + self.populateRenderMeshes(meshTree) + + # if items in listWidget, select those items in the tree + for i in range(listWidget.count()): + item = listWidget.item(i).text() + matches = meshTree.findItems(item, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0) + for match in matches: + match.setSelected(True) + + # show the window + mainWin.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addMeshesToLodList(self, listWidget, meshTree, mainWin, label): + """ + Finds the selected items in addMeshToList_UI's treeWidget, and adds them back to the main LOD page's + listWidget for meshes associated with that LOD. + + :param listWidget: The list widget to add selected items in the treeWidget to. + :param meshTree: The treeWidget from addMeshToList_UI(), whose selection will be queried. + :param mainWin: The window instance from addMeshToList_UI() + :param label: The name of the LOD tab + + .. seealso:: ART_ExportMeshes.saveMeshList() + + """ + + # add to list widget + selected = meshTree.selectedItems() + listWidget.clear() + for each in selected: + icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png"))) + listWidgetItem = QtWidgets.QListWidgetItem(icon, each.text(0)) + listWidget.addItem(listWidgetItem) + + # hook up connections + self.saveMeshList(listWidget, label) + + # close window + mainWin.close() + mainWin.deleteLater() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def saveLodPose(self, lod): + """ + Queries the joints in the skeleton and get the attribute values to store for the given lod. + + :param lod: the text label of the LOD to operate on. + + .. todo:: Suggested feature request was to have this operate on the rig controls and not just joints. That + would mean storing both controls and joints. The desire behind this was to do all LOD posing in the + rig file, rather than the temp file that gets created for exporting. + + """ + + # get all joints + modules = utils.returnRigModules() + + # go through each one, and find the created bones for that modules + createdJoints = [] + for module in modules: + if module != "ART_Root_Module": + joints = cmds.getAttr(module + ".Created_Bones") + splitJoints = joints.split("::") + + for bone in splitJoints: + if bone != "": + createdJoints.append(bone) + + dataDict = {} + for each in createdJoints: + translateData = cmds.getAttr(each + ".translate")[0] + translate = [] + for d in translateData: + translate.append(float('{:.3f}'.format(d))) + + rotateData = cmds.getAttr(each + ".rotate")[0] + rotate = [] + for r in rotateData: + rotate.append(float('{:.3f}'.format(r))) + + scaleData = cmds.getAttr(each + ".scale")[0] + scale = [] + for s in scaleData: + scale.append(float('{:.3f}'.format(s))) + + dataDict[str(each)] = [translate, rotate, scale] + + self.lodPoseDict[lod] = dataDict + + for inst in self.mainUI.moduleInstances: + try: + # set the model pose again (un-altered) + inst.setupForRigPose() + inst.setReferencePose("modelPose") + inst.cleanUpRigPose() + except Exception, e: + print e + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def viewLodPose(self, lod, button): + """ + Gather LOD pose attribute data for the given LOD and set those values on the joints. + + :param lod: The text label for the LOD to operate on. + :param button: the "View LOD Pose" button instance + + .. todo:: Suggested feature request was to have this operate on the rig controls and not just joints. That + would mean getting data for both controls and joints. The desire behind this was to do all LOD posing + in the rig file, rather than the temp file that gets created for exporting. + + """ + + if button.isChecked(): + try: + for key in self.lodPoseDict[lod]: + data = self.lodPoseDict[lod].get(key) + if cmds.objExists(key): + for each in data: + # translate, rotate, scale + if each == data[0]: + cmds.setAttr(key + ".translateX", each[0]) + cmds.setAttr(key + ".translateY", each[1]) + cmds.setAttr(key + ".translateZ", each[2]) + + if each == data[1]: + cmds.setAttr(key + ".rotateX", each[0]) + cmds.setAttr(key + ".rotateY", each[1]) + cmds.setAttr(key + ".rotateZ", each[2]) + + if each == data[2]: + cmds.setAttr(key + ".scaleX", each[0]) + cmds.setAttr(key + ".scaleY", each[1]) + cmds.setAttr(key + ".scaleZ", each[2]) + + except: + try: + characterNode = utils.returnCharacterModule() + if cmds.objExists(characterNode + "." + lod + "_Pose"): + dict = json.loads(cmds.getAttr(characterNode + "." + lod + "_Pose")) + for key in dict: + data = dict.get(key) + for each in data: + # translate, rotate, scale + if each == data[0]: + cmds.setAttr(key + ".translateX", each[0]) + cmds.setAttr(key + ".translateY", each[1]) + cmds.setAttr(key + ".translateZ", each[2]) + + if each == data[1]: + cmds.setAttr(key + ".rotateX", each[0]) + cmds.setAttr(key + ".rotateY", each[1]) + cmds.setAttr(key + ".rotateZ", each[2]) + + if each == data[2]: + cmds.setAttr(key + ".scaleX", each[0]) + cmds.setAttr(key + ".scaleY", each[1]) + cmds.setAttr(key + ".scaleZ", each[2]) + except Exception, e: + print e + + else: + + for inst in self.mainUI.moduleInstances: + try: + # call on the module's bakeOffsets method + inst.setupForRigPose() + inst.setReferencePose("modelPose") + inst.cleanUpRigPose() + except Exception, e: + print e + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def resetLodPose(self, lod): + """ + Resets the LOD pose for the given LOD to the model pose. + + :param lod: the LOD text label to operate on. + + """ + + for inst in self.mainUI.moduleInstances: + try: + # call on the module's bakeOffsets method + inst.setupForRigPose() + inst.setReferencePose("modelPose") + inst.cleanUpRigPose() + except Exception, e: + print e + + self.saveLodPose(lod) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def lodContextMenu(self, widget, groupBox, point): + """ + Creates a right-click context menu for the weighting transfer entry widget: + + .. image:: /images/xferEntry.png + + :param widget: The parent widget the context menu will spawn from. + :param groupBox: The parent groupBox for the weighting transfer entry. + :param point: Where on the parent widget to spawn the context menu. + + """ + + style = """ + QMenu { + background-color: rgb(60,60,60); + border: 1px solid black; + } + + QMenu::item { + background-color: transparent; + } + + QMenu::item:selected { + background-color: rgb(255,174,0); + color: black; + } + """ + + menu = QtWidgets.QMenu(widget) + menu.setStyleSheet(style) + menu.addAction("Clear Selection", widget.clearSelection) + menu.addAction("Select All", widget.selectAll) + menu.addAction("Remove This Entry", partial(self.removeTransferEntry, groupBox)) + + menu.exec_(widget.mapToGlobal(point)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def removeTransferEntry(self, groupBox): + """ + Removes the given groupBox, deleting a weighting transfer entry. + + :param groupBox: Which groupBox to remove. + + """ + + groupBox.close() + groupBox.deleteLater() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def removeLodTab(self): + """ + Removes the current tab index from the tabWidget. Also removes and LOD attributes associated with this LOD + from the character node. + + """ + + currentTab = self.emPageTabs.currentIndex() + tabText = self.emPageTabs.tabText(currentTab) + + if currentTab != 0: + self.emPageTabs.removeTab(currentTab) + + # remove LOD attrs + characterNode = utils.returnCharacterModule() + attrs = cmds.listAttr(characterNode, ud=True, string=tabText + "*") + if attrs is not None: + for attr in attrs: + cmds.deleteAttr(characterNode, at=attr) + + else: + msgBox = QtWidgets.QMessageBox() + msgBox.setText("Cannot remove LOD 0") + msgBox.setIcon(QtWidgets.QMessageBox.Critical) + msgBox.exec_() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def browseToFBX(self, lineEdit): + """ + Calls on a fileDialog for the user to browse to an FBX file for saving. Either one that exists, or creating + a new one. + + :param lineEdit: The QLineEdit whose text to set with the path to the FBX file. + + """ + + fileName = cmds.fileDialog2(fm=0, dir=self.projectPath, ff="*.fbx") + fileName = utils.returnFriendlyPath(fileName[0]) + lineEdit.setText(fileName) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addItemsToWeightXferList(self, widget, boneWidget): + """ + Takes the items selected in the skeleton tree (pictured below) and auto-selects and children under the + selected items, as removing the parent will also remove the children of those parents, and then adds all of + those items to the given widget. + + .. image:: /images/boneTree.png + + :param widget: The QListWidget in the weighting transfer entry widget showing bones to remove. + :param boneWidget: The QListWidget in the weighting transfer entry widget showing bone who will receive + weights from removed bones. + + .. image:: /images/xferEntry.png + + .. seealso:: ART_ExportMeshes.addXferBoneToList() + + """ + + # get selected items in self.tree + selected = self.tree.selectedItems() + + fullList = [] + itemList = [] + for each in selected: + self.findTreeChildren(fullList, itemList, each) + + for item in itemList: + item.setSelected(True) + + for each in fullList: + widget.addItem(each) + + self.addXferBoneToList(boneWidget) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findTreeChildren(self, fullList, itemList, item): + """ + Finds any child items from selected items in the Skeleton Tree and appends them to the input lists. + + :param fullList: The full list of all items, including the original selected parent items and any child items + :param itemList: The instances in memory of the selected items in the Skeleton Tree. + :param item: The parent item in the Skeleton Tree to check for children + + .. seealso:: ART_ExportMeshes.addItemsToWeightXferList() + + """ + + fullList.append(item.text(0)) + itemList.append(item) + for i in range(item.childCount()): + self.findTreeChildren(fullList, itemList, item.child(i)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addXferBoneToList(self, widget, autoFind=True): + """ + Finds the selected items in the Skeleton Tree (for removal) and locates the next viable parent to transfer + weighting to, then adds that bone to the passed in QListWidget. + + :param widget: The QListWidget to add the bone that will receive weights from removed bones. + :param autoFind: Whether or not to auto-locate the next viable parent or use the currently selected item. + + """ + + if autoFind: + # find selected items, find the first item parent that is not selected + selected = self.tree.selectedItems() + + viableParent = None + for each in selected: + if each.parent().isSelected() is False: + viableParent = each.parent().text(0) + widget.clear() + widget.addItem(viableParent) + + else: + + selected = self.tree.selectedItems() + if len(selected) > 1: + + msgBox = QtWidgets.QMessageBox() + msgBox.setText("Only 1 bone can be selected to transfer weighting to.") + msgBox.setIcon(QtWidgets.QMessageBox.Critical) + msgBox.exec_() + + else: + widget.clear() + widget.addItem(selected[0].text(0)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def removeBonesFromList(self, widget): + """ + Takes the selected items from the left QListWidget in a weighting transfer widget and attempts to remove + them from the list. + + :param widget: The QListWidget to check for selected items in. + + """ + + selected = widget.selectedItems() + + # find the text of each selected item and list items in the main skeleton tree that match that text + for each in selected: + text = each.text() + match = self.tree.findItems(text, QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive, 0) + + # check to make sure (if there was a match) that the parent of that item is not also being removed. + if len(match) > 0: + parent = match[0].parent() + + parentMatch = widget.findItems(parent.text(0), QtCore.Qt.MatchExactly | QtCore.Qt.MatchRecursive) + if len(parentMatch) == 0: + + row = widget.row(each) + widget.takeItem(row) + + else: + msgBox = QtWidgets.QMessageBox() + msgBox.setText( + "Parent of the joint is also being removed. Cannot complete request unless parent joint " + "is not removed.") + msgBox.setIcon(QtWidgets.QMessageBox.Critical) + msgBox.exec_() + + else: + msgBox = QtWidgets.QMessageBox() + msgBox.setText("Was not able to find a matching entry in the skeleton tree.") + msgBox.setIcon(QtWidgets.QMessageBox.Critical) + msgBox.exec_() + + return + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addBoneToListUI_addChildren(self, name, parent): + """ + Populates the Skeleton Tree (self.tree) by recursively looking for relatives of the given name and making more + QTreeWidgetItems using the passed in name, and parenting under the passed in parent. + + :param name: The bone name, which will be used to look for children, and also as the text for the treeWidgetItem + :param parent: The parent treeWidgetItem that the created item will be a child of. + + """ + + item = QtWidgets.QTreeWidgetItem(parent) + item.setText(0, name) + item.setBackground(0, QtCore.Qt.NoBrush) + + children = cmds.listRelatives(name, children=True, type="joint") + if children: + for child in children: + self.addBoneToListUI_addChildren(child, item) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addBoneToList_Accept(self, tree, listWidget, lod, layout, viewBtn): + """ + Called from the "Save and Close" button of the addBoneToList_UI, clears all items in the main listWidget, + then populates that listWidget with the new bones to remove. Adds all LOD pose info to the character node as + well as weighting transfer info for that lod. + + :param listWidget: The listWidget on the main LOD page on the bottom right that lists bones to remove. + :param lod: The LOD text (LOD_#) to operate on. + :param layout: The QVboxLayout to query for weighting transfer entries. + :param viewBtn: The "View LOD Pose" button instance + + """ + + # find items already in listWidget + listWidget.clear() + + # get the character node + characterNode = utils.returnCharacterModule() + + # add info to characterNode + if not cmds.objExists(characterNode + "." + lod + "_Pose"): + cmds.addAttr(characterNode, ln=lod + "_Pose", dt="string") + + if not cmds.objExists(characterNode + "." + lod + "_Bones"): + cmds.addAttr(characterNode, ln=lod + "_Bones", dt="string") + + # gather weight transfer info and pose info + lodXferList = [] + + for i in range(layout.count()): + if type(layout.itemAt(i).widget()) == QtWidgets.QGroupBox: + children = layout.itemAt(i).widget().children() + data = [] + removeBones = [] + for each in children: + if type(each) == QtWidgets.QListWidget: + + # [[list of bones], xferbone] + if each.property("list") == "remove": + + for x in range(each.count()): + item = each.item(x) + if item.text() not in removeBones: + removeBones.append(item.text()) + + elif each.property("list") == "xfer": + for y in range(each.count()): + item = each.item(y) + data.append(item.text()) + data.append(removeBones) + lodXferList.append(data) + + for item in lodXferList: + bones = item[1] + + for bone in bones: + icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/boneDisplay.png"))) + listWidgetItem = QtWidgets.QListWidgetItem(icon, str(bone)) + listWidget.addItem(listWidgetItem) + + string = json.dumps(lodXferList) + cmds.setAttr(characterNode + "." + lod + "_Bones", string, type="string") + + # Pose Info + try: + poseString = json.dumps(self.lodPoseDict[lod]) + cmds.setAttr(characterNode + "." + lod + "_Pose", poseString, type="string") + except: + print "no LOD pose given. Using model pose." + + # set viewPoseBtn to unchecked + viewBtn.setChecked(False) + self.viewLodPose(lod, viewBtn) + + # remove UI + if cmds.window("ART_AddBoneToLODlistWin", exists=True): + cmds.deleteUI("ART_AddBoneToLODlistWin", wnd=True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateRenderMeshes(self, tree): + """ + Finds all skinned meshes in the scene and adds them as items to the given TreeWidget. + + .. image:: /images/addMeshesUI.png + + :param tree: The QTreeWidget to add found meshes to + + """ + + # find all skinned meshes in scene + skinClusters = cmds.ls(type='skinCluster') + renderMeshes = [] + + for cluster in skinClusters: + geometry = cmds.skinCluster(cluster, q=True, g=True)[0] + geoTransform = cmds.listRelatives(geometry, parent=True)[0] + renderMeshes.append(geoTransform) + + geoParents = [] + worldGeo = [] + meshDict = {} + + for geo in renderMeshes: + parent = cmds.listRelatives(geo, parent=True) + if parent is not None: + parent = parent[0] + + if parent not in geoParents: + geoParents.append(parent) + + else: + worldGeo.append(geo) + + for each in geoParents: + children = cmds.listRelatives(each) + meshDict[each] = children + + for key in meshDict.keys(): + item = QtWidgets.QTreeWidgetItem(tree) + item.setText(0, key) + item.setBackground(0, QtCore.Qt.NoBrush) + + children = meshDict.get(key) + for child in children: + icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png"))) + childItem = QtWidgets.QTreeWidgetItem(item) + childItem.setText(0, child) + childItem.setBackground(0, QtCore.Qt.NoBrush) + childItem.setIcon(0, icon) + + for geo in worldGeo: + icon = QtGui.QIcon(utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png"))) + geoItem = QtWidgets.QTreeWidgetItem(tree) + geoItem.setText(0, geo) + geoItem.setBackground(0, QtCore.Qt.NoBrush) + geoItem.setIcon(0, icon) + + tree.expandAll() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateUI(self): + """ + Checks to see if LOD attributes exist on the character node, and if so, builds and populates the UI based on + those settings. + + """ + + # get the character node + characterNode = utils.returnCharacterModule() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # populate ui automatically if the settings exist + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + attrs = cmds.listAttr(characterNode, ud=True, string="LOD_*_FilePath") + lodAttrs = [] + createdTabs = [] + if attrs is not None: + for attr in attrs: + lodAttrs.append(attr.partition("_FilePath")[0]) + + if lodAttrs is not None: + # get json data from attribute + lodData = ["_Pose", "_Bones", "_Meshes", "_FilePath"] + meshValue = None + boneValue = None + pathValue = None + + for attr in lodAttrs: + for entry in lodData: + if cmds.objExists(characterNode + "." + attr + entry): + if entry == "_Bones": + boneValue = json.loads(cmds.getAttr(characterNode + "." + attr + entry)) + if entry == "_Meshes": + meshValue = cmds.listConnections(characterNode + "." + attr + entry) + if entry == "_FilePath": + pathValue = json.loads(cmds.getAttr(characterNode + "." + attr + entry)) + + # compare number of LOD attrs to number of current tabs. If needed, add more tabs for the lods + numTabs = self.emPageTabs.count() + + if numTabs < len(lodAttrs): + for i in range(numTabs): + tab = self.emPageTabs.widget(i) + tabText = self.emPageTabs.tabText(i) + + if attr != tabText: + if attr not in createdTabs: + self.createLODpage(attr, True) + createdTabs.append(attr) + + # find the associated tab and set the data + for i in range(self.emPageTabs.count()): + tab = self.emPageTabs.widget(i) + tabText = self.emPageTabs.tabText(i) + + if tabText == attr: + # tab children + lists = lineEdits = tab.findChildren(QtWidgets.QListWidget) + lineEdits = tab.findChildren(QtWidgets.QLineEdit) + + # set meshes and bones for this tab + for list in lists: + property = list.property("listType") + + if property == "mesh": + if meshValue is not None: + for mesh in meshValue: + icon = QtGui.QIcon( + utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/mesh.png"))) + listWidgetItem = QtWidgets.QListWidgetItem(icon, mesh) + list.addItem(listWidgetItem) + + if property == "bone": + if boneValue is not None: + for item in boneValue: + bones = item[1] + for bone in bones: + icon = QtGui.QIcon(utils.returnFriendlyPath( + os.path.join(self.iconsPath, "System/boneDisplay.png"))) + listWidgetItem = QtWidgets.QListWidgetItem(icon, bone) + list.addItem(listWidgetItem) + + # set file path + if os.path.exists(os.path.dirname(pathValue)): + lineEdits[0].setText(pathValue) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def saveFilePath(self, lineEdit, tabText, *args): + """ + Gathers the FBX output path for the LOD and stores that information to the character node. + + :param lineEdit: The QLineEdit which stores the output path text. + :param tabText: The LOD text to operate on (LOD_#) + + """ + + characterNode = utils.returnCharacterModule() + + # get file path + path = lineEdit.text() + dataString = json.dumps(path) + + if not cmds.objExists(characterNode + "." + tabText + "_FilePath"): + cmds.addAttr(characterNode, ln=tabText + "_FilePath", dt="string") + + cmds.setAttr(characterNode + "." + tabText + "_FilePath", dataString, type="string") + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def saveMeshList(self, listWidget, tabText): + """ + Gathers the associated meshes for an LOD and stores that information to the character node. + + :param listWidget: The QListWidget of the LOD tab to search for associated meshes. + :param tabText: The LOD text to operate on (LOD_#) + + """ + + characterNode = utils.returnCharacterModule() + + if not cmds.objExists(characterNode + "." + tabText + "_Meshes"): + cmds.addAttr(characterNode, ln=tabText + "_Meshes", at="message") + if not cmds.objExists(characterNode + "." + tabText + "_FilePath"): + cmds.addAttr(characterNode, ln=tabText + "_FilePath", dt="string") + + i = tabText.partition("_")[2] + + # first remove all connections + connections = cmds.listConnections(characterNode + "." + tabText + "_Meshes") + if connections is not None: + for conn in connections: + cmds.disconnectAttr(characterNode + "." + tabText + "_Meshes", conn + ".lodGroup" + str(i)) + + # Add attrs to meshes to connect up to the characterNode LOD meshes attr + meshList = [] + for x in range(listWidget.count()): + meshList.append(listWidget.item(x).text()) + + for mesh in meshList: + if not cmds.objExists(mesh + ".lodGroup" + str(i)): + cmds.addAttr(mesh, sn="lodGroup" + str(i), at="message") + + cmds.connectAttr(characterNode + "." + tabText + "_Meshes", mesh + ".lodGroup" + str(i)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def export(self): + """ + Gathers all export data by calling on utils.findExportMeshData, parses the information, and for each LOD in + the list, calls on utils.ExportMesh(), passing in the appropriate data. + + .. seealso:: utils.ExportMesh(), utils.findExportMeshData() + + """ + + exportData = utils.findExportMeshData() + for each in exportData: + if not os.path.exists(os.path.dirname(each[0])): + msgBox = QtWidgets.QMessageBox() + msgBox.setText("There are LODs with no valid file path to export to. Aborting.") + msgBox.setIcon(QtWidgets.QMessageBox.Critical) + msgBox.exec_() + return + if each[1] is None: + msgBox = QtWidgets.QMessageBox() + msgBox.setText("There are LODs that have no meshes associated with them. Aborting.") + msgBox.setIcon(QtWidgets.QMessageBox.Critical) + msgBox.exec_() + return + + # save the file + saveFile = cmds.file(q=True, sceneName=True) + try: + cmds.file(save=True) + except Exception, e: + cmds.error("Could not save file. Error: " + str(e)) + return + + # export + fileData = [] + for each in exportData: + meshValue = each[1] + pathValue = each[0] + boneValue = each[2] + poseData = each[3] + + utils.exportMesh(self.mainUI, meshValue, pathValue, boneValue, poseData) + fileData.append(pathValue) + + # open the file + cmds.file(saveFile, open=True, force=True) + + # report and close + self.close() + + msgBox = QtWidgets.QMessageBox() + msgBox.setText("Export Complete!") + + string = "" + for each in fileData: + string += each + "\n" + msgBox.setDetailedText(string) + msgBox.setIcon(QtWidgets.QMessageBox.Information) + msgBox.exec_() diff --git a/Core/Scripts/Interfaces/ART_ExportMotionUI.py b/Core/Scripts/Interfaces/ART_ExportMotionUI.py new file mode 100644 index 0000000..10f9943 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ExportMotionUI.py @@ -0,0 +1,1212 @@ +""" +Author: Jeremy Ernst +""" + +# import statements +import json +import os +import subprocess +import tempfile +from functools import partial + +import System.interfaceUtils as interfaceUtils +import System.utils as utils +import maya.cmds as cmds +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_ExportMotion(object): + """ + This class is used to export FBX animation from the rig to Unreal Engine. It supports morph targets, + custom attribute curves, and pre/post scripts. + + It can be found on the animation sidebar with this icon: + .. image:: /images/exportMotionButton.png + + .. todo:: Add the ability to export alembic and animation curve data. + + """ + + def __init__(self, animPickerUI, parent=None): + """ + Instantiates the class, getting the QSettings and calling on the function to build the interface. + + :param animPickerUI: Instance of the Animation UI from which this class was called. + + """ + + super(ART_ExportMotion, self).__init__() + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.pickerUI = animPickerUI + + # write out qss based on user settings + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir, sheet)) + + # build the UI + self.buildUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + + if cmds.window("pyART_ExportMotionWIN", exists=True): + cmds.deleteUI("pyART_ExportMotionWIN", wnd=True) + + # create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + self.mainWin.resizeEvent = self.windowResized + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + # create the mainLayout + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.mainWin.setStyleSheet(self.style) + + self.mainWin.setMinimumSize(QtCore.QSize(470, 500)) + self.mainWin.setMaximumSize(QtCore.QSize(470, 900)) + self.mainWin.resize(470, 500) + + # set qt object name + self.mainWin.setObjectName("pyART_ExportMotionWIN") + self.mainWin.setWindowTitle("Export Motion") + + # tabs + self.exportTabs = QtWidgets.QTabWidget() + self.layout.addWidget(self.exportTabs) + + # style sheet + stylesheet = """ + QTabBar::tab + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + width: 100px; + padding-left: -10px; + } + QTabBar::tab:selected + { + background-color: rgb(14,100,143); + border: 2px solid black; + } + QTabBar::tab:hover + { + background: rgb(19,132,183); + } + QTabBar::tab:!selected + { + margin-top: 5px; + border: 2px solid black; + } + QTabWidget::pane + { + border-top: 2px solid rgb(19,132,183); + border-left: 2px solid rgb(19,132,183); + border-right: 2px solid rgb(19,132,183); + border-bottom: 2px solid rgb(19,132,183); + } + """ + + stylesheet2 = """ + QTabBar::tab + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + width: 140px; + padding-left: -10px; + } + QTabBar::tab:selected + { + background-color: rgb(14,100,143); + border: 2px solid black; + } + QTabBar::tab:hover + { + background: rgb(19,132,183); + } + QTabBar::tab:!selected + { + margin-top: 5px; + } + QTabWidget::pane + { + border-top: 2px solid rgb(255,175,25); + border-left: 0px solid rgb(19,132,183); + border-right: 0px solid rgb(19,132,183); + border-bottom: 2px solid rgb(255,175,25); + } + """ + + self.exportTabs.setStyleSheet(stylesheet) + + # FBX Tab + self.fbxExportTab = QtWidgets.QWidget() + self.exportTabs.addTab(self.fbxExportTab, "FBX") + + # ABC Tab + self.abcExportTab = QtWidgets.QWidget() + self.exportTabs.addTab(self.abcExportTab, "ABC") + + # Anim Curve Tab + self.animExportTab = QtWidgets.QWidget() + self.exportTabs.addTab(self.animExportTab, "Animation") + + # ======================================================================= + # ======================================================================= + # ======================================================================= + # ======================================================================= + # #FBX TAB + # ======================================================================= + # ======================================================================= + # ======================================================================= + # ======================================================================= + self.fbxTabLayoutFrame = QtWidgets.QFrame(self.fbxExportTab) + self.fbxTabLayoutFrame.setObjectName("dark") + self.fbxTabLayoutFrame.setMinimumSize(450, 410) + self.fbxTabLayoutFrame.setMaximumSize(450, 900) + self.fbxTabLayoutFrame.setStyleSheet(self.style) + + self.fbxTabLayout = QtWidgets.QVBoxLayout(self.fbxTabLayoutFrame) + + # FBX Export Tabs + self.fbxTabs = QtWidgets.QTabWidget() + self.fbxTabLayout.addWidget(self.fbxTabs) + + self.fbxTabs.setStyleSheet(stylesheet2) + + # Settings Tab + self.exportSettings = QtWidgets.QWidget() + self.fbxTabs.addTab(self.exportSettings, "Export Settings") + self.settingsTabLayout = QtWidgets.QVBoxLayout(self.exportSettings) + + # Anim Curve Tab + self.sequencesTab = QtWidgets.QWidget() + self.fbxTabs.addTab(self.sequencesTab, "Sequences") + self.sequenceTabLayout = QtWidgets.QVBoxLayout(self.sequencesTab) + + # ======================================================================= + # ======================================================================= + # ======================================================================= + # # Export Settings + # ======================================================================= + # ======================================================================= + # ======================================================================= + self.exportSettings = QtWidgets.QFrame() + self.exportSettings.setObjectName("dark") + self.exportSettings.setMinimumSize(QtCore.QSize(415, 330)) + self.exportSettings.setMaximumSize(QtCore.QSize(415, 900)) + self.settingsTabLayout.addWidget(self.exportSettings) + + self.settingsLayout = QtWidgets.QVBoxLayout(self.exportSettings) + + # export meshes checkbox + self.exportMeshCB = QtWidgets.QCheckBox("Export Meshes") + self.settingsLayout.addWidget(self.exportMeshCB) + self.exportMeshCB.setChecked(True) + + # horizontal layout for morphs and custom attr curves + self.settings_cb_layout = QtWidgets.QHBoxLayout() + self.settingsLayout.addLayout(self.settings_cb_layout) + + # export morphs and custom attr curves checkboxes + self.exportMorphsCB = QtWidgets.QCheckBox("Export Morph Targets") + self.settings_cb_layout.addWidget(self.exportMorphsCB) + self.exportMorphsCB.setChecked(True) + + self.exportCustomAttrsCB = QtWidgets.QCheckBox("Export Custom Attribute Curves") + self.settings_cb_layout.addWidget(self.exportCustomAttrsCB) + self.exportCustomAttrsCB.setChecked(True) + + # horizontal layout for list widgets (morphs and custom attrs) + self.settings_list_layout = QtWidgets.QHBoxLayout() + self.settingsLayout.addLayout(self.settings_list_layout) + + # list widgets for listing morphs and custom attr curves + self.exportMorphList = QtWidgets.QListWidget() + self.exportMorphList.setMinimumSize(QtCore.QSize(185, 150)) + self.exportMorphList.setMaximumSize(QtCore.QSize(185, 150)) + self.settings_list_layout.addWidget(self.exportMorphList) + self.exportMorphList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + + self.exportCurveList = QtWidgets.QListWidget() + self.exportCurveList.setMinimumSize(QtCore.QSize(185, 150)) + self.exportCurveList.setMaximumSize(QtCore.QSize(185, 150)) + self.settings_list_layout.addWidget(self.exportCurveList) + self.exportCurveList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + + # signal slots for checkboxes and lists + self.exportMorphsCB.stateChanged.connect(partial(self.disableWidget, self.exportMorphList, self.exportMorphsCB)) + self.exportCustomAttrsCB.stateChanged.connect( + partial(self.disableWidget, self.exportCurveList, self.exportCustomAttrsCB)) + self.exportMorphsCB.stateChanged.connect(self.exportMeshCB.setChecked) + self.exportMeshCB.stateChanged.connect(self.exportMorphsCB.setChecked) + + # horizontal layout for pre-script + self.preScript_layout = QtWidgets.QHBoxLayout() + self.settingsLayout.addLayout(self.preScript_layout) + + # pre-script checkbox, lineEdit, and button + self.preScriptCB = QtWidgets.QCheckBox("Pre-Script: ") + self.preScript_layout.addWidget(self.preScriptCB) + + self.preScript_path = QtWidgets.QLineEdit() + self.preScript_layout.addWidget(self.preScript_path) + + ps_browseBtn = QtWidgets.QPushButton() + ps_browseBtn.setMinimumSize(25, 25) + ps_browseBtn.setMaximumSize(25, 25) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/fileBrowse.png")) + ps_browseBtn.setIconSize(QtCore.QSize(25, 25)) + ps_browseBtn.setIcon(icon) + ps_browseBtn.clicked.connect(partial(self.fileBrowse_script, self.preScript_path, self.preScriptCB)) + self.preScript_layout.addWidget(ps_browseBtn) + + # horizontal layout for post-script + self.postScript_layout = QtWidgets.QHBoxLayout() + self.settingsLayout.addLayout(self.postScript_layout) + + # pre-script checkbox, lineEdit, and button + self.postScriptCB = QtWidgets.QCheckBox("Post-Script: ") + self.postScript_layout.addWidget(self.postScriptCB) + + self.postScript_path = QtWidgets.QLineEdit() + self.postScript_layout.addWidget(self.postScript_path) + + pps_browseBtn = QtWidgets.QPushButton() + pps_browseBtn.setMinimumSize(25, 25) + pps_browseBtn.setMaximumSize(25, 25) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/fileBrowse.png")) + pps_browseBtn.setIconSize(QtCore.QSize(25, 25)) + pps_browseBtn.setIcon(icon) + pps_browseBtn.clicked.connect(partial(self.fileBrowse_script, self.postScript_path, self.postScriptCB)) + self.postScript_layout.addWidget(pps_browseBtn) + + # save settings button + button = QtWidgets.QPushButton("Save Export Settings") + self.settingsLayout.addWidget(button) + button.clicked.connect(self.fbx_saveExportData) + + # spacer + self.settingsLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + # ======================================================================= + # ======================================================================= + # ======================================================================= + # # FBX Sequences + # ======================================================================= + # ======================================================================= + # ======================================================================= + + # # Add Sequence + self.addFbxAnimSequence = QtWidgets.QPushButton("Add Sequence") + self.addFbxAnimSequence.setMinimumSize(QtCore.QSize(415, 50)) + self.addFbxAnimSequence.setMaximumSize(QtCore.QSize(415, 50)) + self.addFbxAnimSequence.setObjectName("blueButton") + self.addFbxAnimSequence.clicked.connect(partial(self.fbx_addSequence)) + self.sequenceTabLayout.addWidget(self.addFbxAnimSequence) + + # #Main Export Section + self.fbxAnimSequenceFrame = QtWidgets.QFrame() + self.fbxAnimSequenceFrame.setMinimumWidth(415) + self.fbxAnimSequenceFrame.setMaximumWidth(415) + scrollSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) + self.fbxAnimSequenceFrame.setSizePolicy(scrollSizePolicy) + self.fbxAnimSequenceFrame.setObjectName("dark") + + self.fbxMainScroll = QtWidgets.QScrollArea() + self.sequenceTabLayout.addWidget(self.fbxMainScroll) + self.fbxMainScroll.setMinimumSize(QtCore.QSize(415, 280)) + self.fbxMainScroll.setMaximumSize(QtCore.QSize(415, 900)) + self.fbxMainScroll.setWidgetResizable(True) + self.fbxMainScroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.fbxMainScroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.fbxMainScroll.setWidget(self.fbxAnimSequenceFrame) + + self.fbxSequenceLayout = QtWidgets.QVBoxLayout(self.fbxAnimSequenceFrame) + self.fbxSequenceLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + # spacer + self.sequenceTabLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + # ======================================================================= + # #export button + # ======================================================================= + self.doFbxExportBtn = QtWidgets.QPushButton("Export") + self.fbxTabLayout.addWidget(self.doFbxExportBtn) + self.doFbxExportBtn.setObjectName("blueButton") + self.doFbxExportBtn.setMinimumSize(QtCore.QSize(430, 40)) + self.doFbxExportBtn.setMaximumSize(QtCore.QSize(430, 40)) + self.doFbxExportBtn.clicked.connect(self.fbx_export) + + # show window + self.mainWin.show() + self.fbxTabs.setCurrentIndex(1) + + # find morphs + self.findMorphs() + + # find custom curves + self.findCustomCurves() + + # populate UI + self.fbx_populateUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_checkExportMesh(self): + + if self.exportMorphsCB.isChecked(): + if not self.exportMeshCB.isChecked(): + self.exportMeshCB.setChecked(True) + self.exportMeshCB.setEnabled(False) + + else: + self.exportMeshCB.setEnabled(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_populateUI(self, refresh=False, *args): + + # remove existing animation sequences + widgetsToRemove = [] + + for i in range(self.fbxSequenceLayout.count()): + child = self.fbxSequenceLayout.itemAt(i) + if child is not None: + if type(child.widget()) == QtWidgets.QGroupBox: + widgetsToRemove.append(child.widget()) + + for widget in widgetsToRemove: + self.fbx_removeAnimSequence(widget) + + # get characters in scene + characters = [] + characterInfo = self.findCharacters() + for info in characterInfo: + characters.append(info[0]) + + # check character nodes for fbxAnimData + for currentChar in characters: + # loop through data, adding sequences and setting settings + if cmds.objExists(currentChar + ":ART_RIG_ROOT.fbxAnimData"): + fbxData = json.loads(cmds.getAttr(currentChar + ":ART_RIG_ROOT.fbxAnimData")) + + # each entry in the fbxData list is a sequence with all the needed information + for data in fbxData: + + # first, set export settings + self.exportMeshCB.setChecked(data[0]) + self.exportMorphsCB.setChecked(data[1]) + self.exportCustomAttrsCB.setChecked(data[2]) + + # select morphs and curves to export in the lists if they exist + for i in range(self.exportMorphList.count()): + bShape = self.exportMorphList.item(i) + text = bShape.text() + if text in data[3]: + bShape.setSelected(True) + + for i in range(self.exportCurveList.count()): + cCurve = self.exportCurveList.item(i) + text = cCurve.text() + if text in data[4]: + cCurve.setSelected(True) + + # set pre/post script info + self.preScriptCB.setChecked(data[5][0]) + self.preScript_path.setText(data[5][1]) + + self.postScriptCB.setChecked(data[6][0]) + self.postScript_path.setText(data[6][1]) + + # add anim sequence + self.fbx_addSequence(data[7]) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_addSequence(self, data=None): + + # get number of children of fbxSequenceLayout + children = self.fbxSequenceLayout.count() + index = children - 1 + + # contained groupBox for each sequence + groupBox = QtWidgets.QGroupBox() + groupBox.setCheckable(True) + groupBox.setMaximumHeight(260) + groupBox.setMaximumWidth(380) + self.fbxSequenceLayout.insertWidget(index, groupBox) + + # set context menu policy on groupbox + groupBox.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + groupBox.customContextMenuRequested.connect(partial(self.fbx_createContextMenu, groupBox)) + + # add frame layout to groupbox + frameLayout = QtWidgets.QVBoxLayout(groupBox) + + # add frame to groupbox + frame = QtWidgets.QFrame() + frame.setObjectName("light") + frameLayout.addWidget(frame) + + vLayout = QtWidgets.QVBoxLayout(frame) + vLayout.setObjectName("vLayout") + + # signal slot for groupbox checkbox + QtCore.QObject.connect(groupBox, QtCore.SIGNAL("toggled(bool)"), frame.setVisible) + groupBox.setChecked(True) + + # ======================================================================= + # #portrait and character combo box + # ======================================================================= + characterLayout = QtWidgets.QHBoxLayout() + characterLayout.setObjectName("charLayout") + vLayout.addLayout(characterLayout) + + portrait = QtWidgets.QLabel() + portrait.setObjectName("charPortrait") + portrait.setMinimumSize(QtCore.QSize(30, 30)) + portrait.setMaximumSize(QtCore.QSize(30, 30)) + characterLayout.addWidget(portrait) + + characterComboBox = QtWidgets.QComboBox() + characterComboBox.setObjectName("charComboBox") + characterComboBox.setMinimumHeight(30) + characterComboBox.setMaximumHeight(30) + characterLayout.addWidget(characterComboBox) + + # populate combo box + characters = self.findCharacters() + for character in characters: + characterName = character[0] + characterComboBox.addItem(characterName) + self.updateIcon(characterComboBox, portrait, characters) + characterComboBox.currentIndexChanged.connect(partial(self.updateIcon, characterComboBox, portrait, characters)) + + # ======================================================================= + # #Checkbox, path, and browse button + # ======================================================================= + pathLayout = QtWidgets.QHBoxLayout() + pathLayout.setObjectName("pathLayout") + vLayout.addLayout(pathLayout) + + checkBox = QtWidgets.QCheckBox() + checkBox.setObjectName("exportCheckBox") + checkBox.setChecked(True) + pathLayout.addWidget(checkBox) + + pathField = QtWidgets.QLineEdit() + pathField.setObjectName("exportPath") + pathLayout.addWidget(pathField) + pathField.setMinimumWidth(200) + + browseBtn = QtWidgets.QPushButton() + browseBtn.setMinimumSize(25, 25) + browseBtn.setMaximumSize(25, 25) + pathLayout.addWidget(browseBtn) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/fileBrowse.png")) + browseBtn.setIconSize(QtCore.QSize(25, 25)) + browseBtn.setIcon(icon) + browseBtn.clicked.connect(partial(self.fileBrowse_export, pathField)) + + # ======================================================================= + # #frame range, and frame rate + # ======================================================================= + optionLayout = QtWidgets.QHBoxLayout() + vLayout.addLayout(optionLayout) + optionLayout.setObjectName("optionLayout") + + label1 = QtWidgets.QLabel("Start Frame: ") + optionLayout.addWidget(label1) + label1.setStyleSheet("background: transparent;") + + startFrame = QtWidgets.QSpinBox() + startFrame.setObjectName("startFrame") + optionLayout.addWidget(startFrame) + startFrame.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + startFrame.setRange(-1000, 10000) + + label2 = QtWidgets.QLabel(" End Frame: ") + optionLayout.addWidget(label2) + label2.setStyleSheet("background: transparent;") + label2.setAlignment(QtCore.Qt.AlignCenter) + + endFrame = QtWidgets.QSpinBox() + endFrame.setObjectName("endFrame") + optionLayout.addWidget(endFrame) + endFrame.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + endFrame.setRange(-1000, 10000) + + # set frame range by default based on current timeline + start = cmds.playbackOptions(q=True, ast=True) + end = cmds.playbackOptions(q=True, aet=True) + startFrame.setValue(start) + endFrame.setValue(end) + + frameRate = QtWidgets.QComboBox() + frameRate.setObjectName("frameRate") + optionLayout.addWidget(frameRate) + frameRate.hide() + + # add items to frame rate + frameRate.addItem("ntsc") + frameRate.addItem("ntscf") + frameRate.addItem("film") + + # set the FPS to the current scene setting + fps = cmds.currentUnit(q=True, time=True) + if fps == "film": + frameRate.setCurrentIndex(2) + if fps == "ntsc": + frameRate.setCurrentIndex(0) + if fps == "ntscf": + frameRate.setCurrentIndex(1) + + # ======================================================================= + # #advanced options + # ======================================================================= + advancedGroup = QtWidgets.QGroupBox("Advanced Settings") + vLayout.addWidget(advancedGroup) + advancedGroup.setCheckable(True) + + advancedLayout = QtWidgets.QVBoxLayout(advancedGroup) + advancedFrame = QtWidgets.QFrame() + advancedLayout.addWidget(advancedFrame) + advancedFrameLayout = QtWidgets.QVBoxLayout(advancedFrame) + + # ======================================================================= + # #rotation interpolation + # ======================================================================= + interpLayout = QtWidgets.QHBoxLayout() + advancedFrameLayout.addLayout(interpLayout) + + label3 = QtWidgets.QLabel("Rotation Interpolation: ") + interpLayout.addWidget(label3) + label3.setStyleSheet("background: transparent;") + + interpCombo = QtWidgets.QComboBox() + interpLayout.addWidget(interpCombo) + interpCombo.setObjectName("rotInterp") + interpCombo.setMinimumWidth(150) + + interpCombo.addItem("Quaternion Slerp") + interpCombo.addItem("Independent Euler-Angle") + + # ======================================================================= + # #sample rate and root options + # ======================================================================= + rateRootLayout = QtWidgets.QHBoxLayout() + advancedFrameLayout.addLayout(rateRootLayout) + + label4 = QtWidgets.QLabel("Sample Rate: ") + rateRootLayout.addWidget(label4) + + sampleRate = QtWidgets.QDoubleSpinBox() + sampleRate.setObjectName("sampleRate") + rateRootLayout.addWidget(sampleRate) + sampleRate.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + sampleRate.setRange(0, 1) + sampleRate.setSingleStep(0.1) + sampleRate.setValue(1.00) + + rootComboBox = QtWidgets.QComboBox() + rootComboBox.setObjectName("rootExportOptions") + rootComboBox.addItem("Export Root Animation") + rootComboBox.addItem("Zero Root") + rootComboBox.addItem("Zero Root, Keep World Space") + rateRootLayout.addWidget(rootComboBox) + + # signal slot for groupbox checkbox + QtCore.QObject.connect(advancedGroup, QtCore.SIGNAL("toggled(bool)"), advancedFrame.setVisible) + advancedGroup.setChecked(False) + + # signal slot for groupbox title + characterComboBox.currentIndexChanged.connect(partial(self.fbx_updateTitle, groupBox)) + pathField.textChanged.connect(partial(self.fbx_updateTitle, groupBox)) + startFrame.valueChanged.connect(partial(self.fbx_updateTitle, groupBox)) + endFrame.valueChanged.connect(partial(self.fbx_updateTitle, groupBox)) + checkBox.stateChanged.connect(partial(self.fbx_updateTitle, groupBox)) + + # create groupbox title + self.fbx_updateTitle(groupBox) + + # set data if coming from duplicate call + if data: + + # set character combo box + for i in range(characterComboBox.count()): + text = characterComboBox.itemText(i) + if text == data[0]: + characterComboBox.setCurrentIndex(i) + + # set export checkbox + checkBox.setChecked(data[1]) + + # set export path + pathField.setText(data[2]) + + # set start frame + startFrame.setValue(data[3]) + + # set end frame + endFrame.setValue(data[4]) + + # set FPS + for i in range(frameRate.count()): + text = frameRate.itemText(i) + if text == data[5]: + frameRate.setCurrentIndex(i) + + # set rotation interpolation + for i in range(interpCombo.count()): + text = interpCombo.itemText(i) + if text == data[6]: + interpCombo.setCurrentIndex(i) + + # set sample rate + sampleRate.setValue(data[7]) + + # set root export + for i in range(rootComboBox.count()): + text = rootComboBox.itemText(i) + if text == data[8]: + rootComboBox.setCurrentIndex(i) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_createContextMenu(self, widget, point): + + # icons + icon_delete = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/delete.png")) + icon_duplicate = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/duplicate.png")) + icon_collapse = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/upArrow.png")) + icon_expand = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/downArrow.png")) + + # create the context menu + contextMenu = QtWidgets.QMenu() + contextMenu.addAction(icon_delete, "Remove Sequence", partial(self.fbx_removeAnimSequence, widget)) + contextMenu.addAction(icon_duplicate, "Duplicate Sequence", partial(self.fbx_duplicateSequence, widget)) + contextMenu.addAction(icon_expand, "Expand All Sequences", partial(self.fbx_expandAllSequences, True)) + contextMenu.addAction(icon_collapse, "Collapse All Sequences", partial(self.fbx_expandAllSequences, False)) + contextMenu.exec_(widget.mapToGlobal(point)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_removeAnimSequence(self, widget): + + widget.setParent(None) + widget.close() + self.fbx_saveExportData() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_duplicateSequence(self, widget): + + sequenceData = self.fbx_getSequenceInfo(widget) + self.fbx_addSequence(sequenceData) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fileBrowse_export(self, widget): + + try: + path = cmds.fileDialog2(fm=0, okc="Export", dir=self.projectPath, ff="*.fbx") + nicePath = utils.returnFriendlyPath(path[0]) + widget.setText(nicePath) + except: + pass + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fileBrowse_script(self, widget, checkbox=None): + + try: + path = cmds.fileDialog2(fm=1, okc="Accept", dir=self.projectPath, ff="*.py;;*.mel") + nicePath = utils.returnFriendlyPath(path[0]) + widget.setText(nicePath) + + if checkbox is not None: + checkbox.setChecked(True) + except: + pass + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_export(self): + + # save settings + characterData = self.fbx_saveExportData() + + # find mayapy interpreter location + mayapy = utils.getMayaPyLoc() + + # message box for confirming save action + msgBax = QtWidgets.QMessageBox() + msgBax.setText("Please make sure any changes to the current file are saved before continuing.\ + This process will be creating a temporary file to do all of the exporting from.") + msgBax.setIcon(QtWidgets.QMessageBox.Warning) + msgBax.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + msgBax.setDefaultButton(QtWidgets.QMessageBox.Ok) + ret = msgBax.exec_() + + if ret == QtWidgets.QMessageBox.Ok: + + # save copy of scene to temp location + sourceFile = cmds.file(q=True, sceneName=True) + filePath = os.path.dirname(sourceFile) + tempFile = os.path.join(filePath, "export_TEMP.ma") + + cmds.file(rename=tempFile) + cmds.file(save=True, type="mayaAscii", force=True) + + # pass tempFile and characterData to mayapy instance for processing + if os.path.exists(mayapy): + script = utils.returnFriendlyPath(os.path.join(self.toolsPath, "Core\Scripts\System\ART_FbxExport.py")) + + # create a temp file with the json data + with tempfile.NamedTemporaryFile(delete=False) as temp: + json.dump(characterData, temp) + temp.close() + + # create a log file + stdoutFile = os.path.join(filePath, "export_log.txt") + out = file(stdoutFile, 'w') + + # open mayapy, passing in export file and character data + subprocess.Popen(mayapy + ' ' + "\"" + script + "\"" + ' ' + "\"" + tempFile + "\"" + ' ' + + "\"" + temp.name + "\"", stdout=out, stderr=out) + + # close the output file (for logging) + out.close() + + else: + msgBox = QtWidgets.QMessageBox() + msgBox.setText("mayapy executable not found. Currently not implemented for mac and linux.") + msgBox.setIcon(QtWidgets.QMessageBox.Error) + msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + msgBox.setDefaultButton(QtWidgets.QMessageBox.Ok) + msgBox.exec_() + + # reopen the original file + cmds.file(sourceFile, open=True, force=True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findMorphs(self): + + # clear list + self.exportMorphList.clear() + characterMeshes = [] + + # get all characters + characters = self.findCharacters() + + for character in characters: + currentCharacter = character[0] + + # get meshes off of character node + if cmds.objExists(currentCharacter + ":ART_RIG_ROOT"): + if cmds.objExists(currentCharacter + ":ART_RIG_ROOT.LOD_0_Meshes"): + characterMeshes = cmds.listConnections(currentCharacter + ":ART_RIG_ROOT.LOD_0_Meshes") + + # get skinClusters in scene and query their connections + skins = cmds.ls(type="skinCluster") + + for skin in skins: + shapeInfo = cmds.listConnections(skin, c=True, type="blendShape", et=True) + mesh = cmds.listConnections(skin, type="mesh") + + if mesh is not None: + # confirm that the blendshape found belongs to one of our character meshes. Then add to list + if mesh[0] in characterMeshes: + if shapeInfo is not None: + for info in shapeInfo: + if cmds.nodeType(info) == "blendShape": + item = QtWidgets.QListWidgetItem(info) + self.exportMorphList.addItem(item) + item.setSelected(False) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCustomCurves(self): + + # get all characters + characters = self.findCharacters() + + for character in characters: + currentCharacter = character[0] + rootBone = currentCharacter + ":root" + attrs = cmds.listAttr(rootBone, keyable=True) + + standardAttrs = ["translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ", + "scaleX", "scaleY", "scaleZ", "visibility"] + + for attr in attrs: + if attr not in standardAttrs: + item = QtWidgets.QListWidgetItem(currentCharacter + ":" + attr) + self.exportCurveList.addItem(item) + item.setSelected(False) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacters(self): + + characterInfo = [] + + allNodes = cmds.ls(type="network") + characterNodes = [] + for node in allNodes: + attrs = cmds.listAttr(node) + if "rigModules" in attrs: + characterNodes.append(node) + + # go through each node, find the character name, the namespace on the node, and the picker attribute + for node in characterNodes: + try: + namespace = cmds.getAttr(node + ".namespace") + except: + namespace = cmds.getAttr(node + ".name") + + # add the icon found on the node's icon path attribute to the tab + iconPath = cmds.getAttr(node + ".iconPath") + iconPath = utils.returnNicePath(self.projectPath, iconPath) + + characterInfo.append([namespace, iconPath]) + + return characterInfo + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def updateIcon(self, comboBox, label, characterInfo, *args): + + # get current selection of combo box + characterName = comboBox.currentText() + + # loop through characterInfo, find matching characterName, and get icon path + for each in characterInfo: + if characterName == each[0]: + iconPath = each[1] + img = QtGui.QImage(iconPath) + pixmap = QtGui.QPixmap(img.scaledToWidth(30)) + label.setPixmap(pixmap) + label.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def windowResized(self, event): + + currentSize = self.mainWin.size() + height = currentSize.height() + + self.fbxTabLayoutFrame.resize(450, height - 50) + + width = self.fbxTabs.size() + width = width.width() + self.fbxTabs.resize(width, height - 50) + self.fbxMainScroll.setMinimumSize(415, height - 220) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def disableWidget(self, widget, checkbox, *args): + + state = checkbox.isChecked() + if state: + widget.setEnabled(True) + for i in range(widget.count()): + item = widget.item(i) + item.setHidden(False) + else: + widget.setEnabled(False) + for i in range(widget.count()): + item = widget.item(i) + item.setHidden(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_updateTitle(self, groupBox, *args): + + # get info from interface + data = [] + children = groupBox.children() + for each in children: + if type(each) == QtWidgets.QFrame: + contents = each.children() + + for child in contents: + objectName = child.objectName() + + if objectName == "charComboBox": + char = child.currentText() + data.append(char) + + if objectName == "exportCheckBox": + value = child.isChecked() + data.append(value) + + if objectName == "exportPath": + path = child.text() + filename = os.path.basename(path) + data.append(filename.partition(".")[0]) + + if objectName == "startFrame": + startFrame = child.value() + data.append(startFrame) + + if objectName == "endFrame": + endFrame = child.value() + data.append(endFrame) + + titleString = "" + if data[1]: + titleString += data[0] + ", " + titleString += data[2] + ", " + titleString += "[" + titleString += str(data[3]) + ": " + titleString += str(data[4]) + "]" + else: + titleString += "Not Exporting.." + groupBox.setTitle(titleString) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_saveExportData(self): + + exportData = [] + + # get main export settings + exportMesh = self.exportMeshCB.isChecked() + exportMorph = self.exportMorphsCB.isChecked() + exportCurve = self.exportCustomAttrsCB.isChecked() + + exportData.append(exportMesh) + exportData.append(exportMorph) + exportData.append(exportCurve) + + # get selected morphs + morphs = [] + for i in range(self.exportMorphList.count()): + item = self.exportMorphList.item(i) + if item.isSelected(): + morphs.append(item.text()) + + # get selected curves + curves = [] + for i in range(self.exportCurveList.count()): + item = self.exportCurveList.item(i) + if item.isSelected(): + curves.append(item.text()) + + # pre and post script + preScript = self.preScriptCB.isChecked() + preScript_path = self.preScript_path.text() + + postScript = self.postScriptCB.isChecked() + postScript_path = self.postScript_path.text() + + exportData.append(morphs) + exportData.append(curves) + exportData.append([preScript, preScript_path]) + exportData.append([postScript, postScript_path]) + + # get fbx sequences and settings + characterData = {} + for i in range(self.fbxSequenceLayout.count()): + child = self.fbxSequenceLayout.itemAt(i) + if type(child.widget()) == QtWidgets.QGroupBox: + data = [] + sequenceData = self.fbx_getSequenceInfo(child.widget()) + data.extend(exportData) + data.append(sequenceData) + + if sequenceData[0] not in characterData: + characterData[sequenceData[0]] = [data] + else: + currentData = characterData.get(sequenceData[0]) + currentData.append(data) + characterData[sequenceData[0]] = currentData + + # loop through each key (character) in the dictionary, and write its data to the network node + for each in characterData: + data = characterData.get(each) + + # Add that data to the character node + networkNode = each + ":ART_RIG_ROOT" + if not cmds.objExists(networkNode + ".fbxAnimData"): + cmds.addAttr(networkNode, ln="fbxAnimData", dt="string") + + cmds.setAttr(networkNode + ".fbxAnimData", json.dumps(data), type="string") + + return characterData + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_getSequenceInfo(self, groupBox, *args): + + # get info from interface + data = [] + + children = groupBox.children() + for each in children: + if type(each) == QtWidgets.QFrame: + contents = each.children() + + for child in contents: + objectName = child.objectName() + + if objectName == "charComboBox": + char = child.currentText() + data.append(char) + + if objectName == "exportCheckBox": + value = child.isChecked() + data.append(value) + + if objectName == "exportPath": + path = child.text() + data.append(path) + + if objectName == "startFrame": + startFrame = child.value() + data.append(startFrame) + + if objectName == "endFrame": + endFrame = child.value() + data.append(endFrame) + + if objectName == "frameRate": + fps = child.currentText() + data.append(fps) + + if type(child) == QtWidgets.QGroupBox: + subChildren = child.children() + + for sub in subChildren: + if type(sub) == QtWidgets.QFrame: + advancedChildren = sub.children() + for advancedChild in advancedChildren: + advancedObj = advancedChild.objectName() + + if advancedObj == "sampleRate": + rate = advancedChild.value() + data.append(rate) + + if advancedObj == "rotInterp": + interp = advancedChild.currentText() + data.append(interp) + + if advancedObj == "rootExportOptions": + root = advancedChild.currentText() + data.append(root) + + return data + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbx_expandAllSequences(self, state): + + # get info from interface + for i in range(self.fbxSequenceLayout.count()): + data = [] + child = self.fbxSequenceLayout.itemAt(i) + if type(child.widget()) == QtWidgets.QGroupBox: + child.widget().setChecked(state) diff --git a/Core/Scripts/Interfaces/ART_ExportWeights.py b/Core/Scripts/Interfaces/ART_ExportWeights.py new file mode 100644 index 0000000..ad6c9ef --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ExportWeights.py @@ -0,0 +1,335 @@ +""" +Author: Jeremy Ernst + + This class builds the interface for exporting skin weights for each piece of selected geometry. + +=============== +Class +=============== +""" + +import os +from functools import partial + +import maya.cmds as cmds + +import System.riggingUtils as riggingUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_ExportSkinWeights(): + def __init__(self, mainUI): + """ + Instantiate the class, getting the settings from QSettings, then build the interface. + + :param mainUI: instance of the rig creator interface + """ + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + # build the UI + self.buildExportWeightsUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildExportWeightsUI(self): + """ + Build the interface for exporting the skin weights. An entry is added for each piece of selected geometry. + The user then has the ability to specify a .weight file name for the associated geometry. + The user also specifies where they would like the weight files saved to. + + .. image:: /images/exportWeights.png + + """ + + if cmds.window("ART_exportSkinWeightsUI", exists=True): + cmds.deleteUI("ART_exportSkinWeightsUI", wnd=True) + + # launch a UI to get the name information + self.exportSkinWeights_Win = QtWidgets.QMainWindow(self.mainUI) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + # create the main widget + self.exportSkinWeights_mainWidget = QtWidgets.QWidget() + self.exportSkinWeights_Win.setCentralWidget(self.exportSkinWeights_mainWidget) + + # set qt object name + self.exportSkinWeights_Win.setObjectName("ART_exportSkinWeightsUI") + self.exportSkinWeights_Win.setWindowTitle("Export Skin Weights") + + # create the mainLayout for the ui + self.exportSkinWeights_mainLayout = QtWidgets.QVBoxLayout(self.exportSkinWeights_mainWidget) + self.exportSkinWeights_mainLayout.setContentsMargins(5, 5, 5, 5) + + self.exportSkinWeights_Win.resize(450, 600) + self.exportSkinWeights_Win.setSizePolicy(mainSizePolicy) + self.exportSkinWeights_Win.setMinimumSize(QtCore.QSize(450, 600)) + self.exportSkinWeights_Win.setMaximumSize(QtCore.QSize(450, 600)) + + # create the background image + self.exportSkinWeights_frame = QtWidgets.QFrame() + self.exportSkinWeights_mainLayout.addWidget(self.exportSkinWeights_frame) + self.exportSkinWeights_frame.setObjectName("dark") + + # create widgetLayout + self.exportSkinWeights_widgetLayout = QtWidgets.QVBoxLayout(self.exportSkinWeights_frame) + + # create the hboxLayout for lineEdit and browser button + self.exportSkinWeights_browseLayout = QtWidgets.QHBoxLayout() + self.exportSkinWeights_widgetLayout.addLayout(self.exportSkinWeights_browseLayout) + + # create the line edit for the export path + self.exportSkinWeights_lineEdit = QtWidgets.QLineEdit(utils.returnFriendlyPath(self.toolsPath)) + self.exportSkinWeights_browseLayout.addWidget(self.exportSkinWeights_lineEdit) + + self.exportSkinWeights_browseBtn = QtWidgets.QPushButton() + self.exportSkinWeights_browseLayout.addWidget(self.exportSkinWeights_browseBtn) + self.exportSkinWeights_browseBtn.setMinimumSize(35, 35) + self.exportSkinWeights_browseBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/fileBrowse.png")) + self.exportSkinWeights_browseBtn.setIconSize(QtCore.QSize(30, 30)) + self.exportSkinWeights_browseBtn.setIcon(icon) + self.exportSkinWeights_browseBtn.clicked.connect(partial(self.exportSkinWeights_fileBrowse)) + + # scroll area contents + self.exportSkinWeights_scrollContents = QtWidgets.QFrame() + self.exportSkinWeights_scrollContents.setObjectName("light") + + # Layout of Container Widget + self.exportSkinWeights_VLayout = QtWidgets.QVBoxLayout() + + # find selected geometry and populate scroll area + self.exportSkinWeights_populate() + + # add scrollArea for selected geo, skinFileName, and checkbox for exporting + self.exportSkinWeights_scrollLayout = QtWidgets.QScrollArea() + self.exportSkinWeights_widgetLayout.addWidget(self.exportSkinWeights_scrollLayout) + self.exportSkinWeights_scrollLayout.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.exportSkinWeights_scrollLayout.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.exportSkinWeights_scrollLayout.setWidgetResizable(False) + self.exportSkinWeights_scrollLayout.setWidget(self.exportSkinWeights_scrollContents) + + # lastly, export button + font = QtGui.QFont() + font.setPointSize(8) + font.setBold(True) + + self.exportSkinWeights_exportBtnLayout = QtWidgets.QHBoxLayout() + self.exportSkinWeights_widgetLayout.addLayout(self.exportSkinWeights_exportBtnLayout) + + self.exportSkinWeights_RefreshBtn = QtWidgets.QPushButton("Refresh") + self.exportSkinWeights_exportBtnLayout.addWidget(self.exportSkinWeights_RefreshBtn) + self.exportSkinWeights_RefreshBtn.setMinimumSize(QtCore.QSize(70, 50)) + self.exportSkinWeights_RefreshBtn.setMaximumSize(QtCore.QSize(70, 50)) + self.exportSkinWeights_RefreshBtn.setFont(font) + self.exportSkinWeights_RefreshBtn.clicked.connect(partial(self.buildExportWeightsUI)) + self.exportSkinWeights_RefreshBtn.setObjectName("blueButton") + + self.exportSkinWeights_ExportBtn = QtWidgets.QPushButton("EXPORT WEIGHTS") + self.exportSkinWeights_exportBtnLayout.addWidget(self.exportSkinWeights_ExportBtn) + self.exportSkinWeights_ExportBtn.setMinimumSize(QtCore.QSize(350, 50)) + self.exportSkinWeights_ExportBtn.setMaximumSize(QtCore.QSize(350, 50)) + self.exportSkinWeights_ExportBtn.setFont(font) + self.exportSkinWeights_ExportBtn.clicked.connect(partial(self.exportSkinWeights_doExport)) + self.exportSkinWeights_ExportBtn.setObjectName("blueButton") + + # show window + self.exportSkinWeights_Win.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def exportSkinWeights_populate(self): + """ + Populate the interface with an entry for each mesh the user has selected. + + This entry includes the mesh name, an QLineEdit to specify a file name for the .weight file, and a checkbox + as to whether or not the user wants to export weights for that mesh. + + """ + + # get current selection + selection = cmds.ls(sl=True) + if len(selection) > 0: + + # Create headers + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + headerLayout = QtWidgets.QHBoxLayout() + self.exportSkinWeights_VLayout.addLayout(headerLayout) + headerExport = QtWidgets.QLabel(" ") + headerLayout.addWidget(headerExport) + headerExport.setStyleSheet("background: transparent;") + + headerGeo = QtWidgets.QLabel("Mesh") + headerGeo.setMinimumSize(QtCore.QSize(180, 20)) + headerGeo.setMaximumSize(QtCore.QSize(180, 20)) + headerLayout.addWidget(headerGeo) + headerGeo.setFont(font) + headerGeo.setStyleSheet("background: transparent;") + + headerFileName = QtWidgets.QLabel("FileName") + headerLayout.addWidget(headerFileName) + headerFileName.setMinimumSize(QtCore.QSize(180, 20)) + headerFileName.setMaximumSize(QtCore.QSize(180, 20)) + headerFileName.setFont(font) + headerFileName.setStyleSheet("background: transparent;") + + # loop through selection, checking selection is valid and has skinCluster + for each in selection: + + # get dagPath of each + dagPath = cmds.ls(each, long=True)[0] + skinCluster = riggingUtils.findRelatedSkinCluster(dagPath) + + if skinCluster is not None: + # create HBoxLayout + layout = QtWidgets.QHBoxLayout() + layout.setSpacing(10) + self.exportSkinWeights_VLayout.addLayout(layout) + + # create checkbox + checkBox = QtWidgets.QCheckBox() + layout.addWidget(checkBox) + checkBox.setChecked(True) + + # create non editable line edit + niceName = each.rpartition("|")[2] + geoName = QtWidgets.QLabel(niceName + " : ") + geoName.setProperty("dag", dagPath) + layout.addWidget(geoName) + geoName.setMinimumSize(QtCore.QSize(180, 30)) + geoName.setMaximumSize(QtCore.QSize(180, 30)) + + # create editable line edit + if cmds.objExists(dagPath + ".weightFile"): + path = cmds.getAttr(dagPath + ".weightFile") + niceName = path.rpartition("/")[2].partition(".")[0] + dirPath = path.rpartition("/")[0] + dirPath = utils.returnFriendlyPath(dirPath) + self.exportSkinWeights_lineEdit.setText(dirPath) + + skinFileName = QtWidgets.QLineEdit(niceName) + layout.addWidget(skinFileName) + skinFileName.setMinimumSize(QtCore.QSize(170, 30)) + skinFileName.setMaximumSize(QtCore.QSize(170, 30)) + + # add spacer + self.exportSkinWeights_scrollContents.setLayout(self.exportSkinWeights_VLayout) + + else: + label = QtWidgets.QLabel("No Geometry Selected For Export. Select Geometry and Relaunch.") + label.setAlignment(QtCore.Qt.AlignCenter) + self.exportSkinWeights_VLayout.addWidget(label) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def exportSkinWeights_fileBrowse(self): + """ + Open a file dialog that the user can use to browse to the output directory of their choice for saving the + .weight files to. + + """ + + # Need support for defaulting to current project character was last published to and creating character skin + # weights folder + + try: + path = cmds.fileDialog2(fm=3, dir=self.toolsPath)[0] + nicePath = utils.returnFriendlyPath(path) + self.exportSkinWeights_lineEdit.setText(nicePath) + except: + pass + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def exportSkinWeights_doExport(self): + """ + Gather the information from the interface and export the skin weights with that information. + + For each piece of geometry, built the path of the .weight file by joining the output path and the .weight + file name, then call on export_skin_weights from riggingUtils, passing in the .weight file path and the mesh. + + """ + + # get the export path + exportPath = self.exportSkinWeights_lineEdit.text() + value = False + + # find each lineEdit in the scrollArea and get the entered text + for i in range(self.exportSkinWeights_VLayout.count()): + hboxLayout = self.exportSkinWeights_VLayout.itemAt(i) + for x in range(hboxLayout.count()): + widget = hboxLayout.itemAt(x) + + # go through each widget in the hboxLayout, and get values + if type(widget.widget()) == QtWidgets.QLabel: + geoName = widget.widget().text().partition(" :")[0] + dagname = widget.widget().property("dag") + + # see if the user wants to export the weights for this entry + if type(widget.widget()) == QtWidgets.QCheckBox: + value = widget.widget().isChecked() + + # get the fileName + if type(widget.widget()) == QtWidgets.QLineEdit: + fileName = widget.widget().text() + + if fileName.find(":") is not -1: + # name contains invalid characters for file name. + cmds.warning("file name contains invalid characters: ':'") + return + + # create the full path + fullPath = utils.returnNicePath(exportPath, fileName + ".weights") + if value: + # if the checkbox is checked, export skin weights + + # export the skin data + riggingUtils.export_skin_weights(fullPath, dagname) + + # add exportPath attribute to geometry if it doesn't exist + if not cmds.objExists(dagname + ".weightFile"): + cmds.addAttr(dagname, longName="weightFile", dt="string", keyable=True) + cmds.setAttr(dagname + ".weightFile", fullPath, type="string") + + # close the UI + self.exportSkinWeights_Win.close() + + # notify user + try: + cmds.inViewMessage(amg='<hl>All Weights Have Been Exported</hl>.', pos='midCenter', fade=True) + except: + print("All Weights Have Been Exported.") diff --git a/Core/Scripts/Interfaces/ART_FinalizeSetup.py b/Core/Scripts/Interfaces/ART_FinalizeSetup.py new file mode 100644 index 0000000..cfd4e7a --- /dev/null +++ b/Core/Scripts/Interfaces/ART_FinalizeSetup.py @@ -0,0 +1,216 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import System.utils as utils +import System.riggingUtils as riggingUtils + + +class ART_FinalizeSetup(): + def __init__(self, mainUI, skinToolsInst): + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + self.mainUI = mainUI + self.skinToolsInst = skinToolsInst + + #build the UI + self.finalizeSetup_UI() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def finalizeSetup_UI(self): + + if cmds.window("ART_finalizeSetupWin", exists = True): + cmds.deleteUI("ART_finalizeSetupWin", wnd = True) + + #launch a UI to get the name information + self.finalizeSetupWin = QtWidgets.QMainWindow(self.mainUI) + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #load toolbar stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.finalizeSetupWin.setStyleSheet(self.style) + + + #create the main widget + self.finalizeSetupWin_mainWidget = QtWidgets.QWidget() + self.finalizeSetupWin.setCentralWidget(self.finalizeSetupWin_mainWidget) + + #set qt object name + self.finalizeSetupWin.setObjectName("ART_finalizeSetupWin") + self.finalizeSetupWin.setWindowTitle("Finalize Setup") + + #create the mainLayout for the rig creator UI + self.finalizeSetupWin_mainLayout = QtWidgets.QVBoxLayout(self.finalizeSetupWin_mainWidget) + self.finalizeSetupWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.finalizeSetupWin.resize(450, 300) + self.finalizeSetupWin.setSizePolicy(mainSizePolicy) + self.finalizeSetupWin.setMinimumSize(QtCore.QSize( 450, 300 )) + self.finalizeSetupWin.setMaximumSize(QtCore.QSize( 450, 300 )) + + #create the background image + self.finalizeSetupWin_frame = QtWidgets.QFrame() + self.finalizeSetupWin_mainLayout.addWidget(self.finalizeSetupWin_frame) + + + #create the main vertical layout inside the frame + self.finalizeSetupWin_mainVLayout = QtWidgets.QVBoxLayout(self.finalizeSetupWin_frame) + + # # # # TEXT EDIT # # # # + self.finalizeSetupWin_Text = QtWidgets.QTextEdit() + self.finalizeSetupWin_Text.setMinimumSize(QtCore.QSize( 440, 230 )) + self.finalizeSetupWin_Text.setMaximumSize(QtCore.QSize( 440, 230 )) + self.finalizeSetupWin_mainVLayout.addWidget(self.finalizeSetupWin_Text) + self.finalizeSetupWin_Text.setReadOnly(True) + self.finalizeSetupWin_Text.setAcceptRichText(True) + + #text + text = "Finalizing the setup will create the skeleton that will be used for skin binding." + cursor = self.finalizeSetupWin_Text.textCursor() + cursor.insertText(text) + + text = "\nIt is recommended that offsets are baked before continuing. " + self.finalizeSetupWin_Text.setTextColor(QtGui.QColor(236,217,0)) + self.finalizeSetupWin_Text.setFontPointSize(10) + self.finalizeSetupWin_Text.append(text) + + #image + image2 = utils.returnNicePath(self.iconsPath, "System/bakeOffsets.png") + icon = QtGui.QPixmap(image2) + image = icon.toImage() + cursor.insertImage(image) + + + text = "\n(You will still be able to edit your setup by coming back to this step using the 'Edit Setup' button seen in the deformation tools interface):\n\n" + self.finalizeSetupWin_Text.setTextColor(QtGui.QColor(255,255,255)) + self.finalizeSetupWin_Text.setFontPointSize(8) + self.finalizeSetupWin_Text.append(text) + + #image + image2 = utils.returnNicePath(self.iconsPath, "System/finalizeSetup.png") + icon = QtGui.QPixmap(image2) + image = icon.toImage() + cursor.insertImage(image) + + self.finalizeSetupWin_Text.setTextCursor(cursor) + end = "<br>" + fragment = QtGui.QTextDocumentFragment.fromHtml(end) + cursor.insertFragment(fragment) + # # # # END TEXT EDIT # # # # + + + + + # # # # BUTTON LAYOUT # # # # + self.finalizeSetupWin_buttonLayout = QtWidgets.QHBoxLayout() + self.finalizeSetupWin_mainVLayout.addLayout(self.finalizeSetupWin_buttonLayout) + + self.finalizeSetupWin_ContinueBtn = QtWidgets.QPushButton("Continue") + self.finalizeSetupWin_CancelBtn = QtWidgets.QPushButton("Cancel") + self.finalizeSetupWin_HelpBtn = QtWidgets.QPushButton("?") + self.finalizeSetupWin_HelpBtn.setMinimumSize(QtCore.QSize( 25, 25 )) + self.finalizeSetupWin_HelpBtn.setMaximumSize(QtCore.QSize( 25, 25 )) + self.finalizeSetupWin_buttonLayout.addWidget(self.finalizeSetupWin_ContinueBtn) + self.finalizeSetupWin_buttonLayout.addWidget(self.finalizeSetupWin_CancelBtn) + self.finalizeSetupWin_buttonLayout.addWidget(self.finalizeSetupWin_HelpBtn) + + self.finalizeSetupWin_ContinueBtn.clicked.connect(partial(self.finalizeSetup_Continue)) + self.finalizeSetupWin_CancelBtn.clicked.connect(partial(self.finalizeSetup_Cancel)) + self.finalizeSetupWin_HelpBtn.clicked.connect(partial(self.finalizeSetup_Help)) + + self.finalizeSetupWin_ContinueBtn.setObjectName("blueButton") + self.finalizeSetupWin_CancelBtn.setObjectName("blueButton") + self.finalizeSetupWin_HelpBtn.setObjectName("blueButton") + # # # # END BUTTON LAYOUT # # # # + + + #show window + self.finalizeSetupWin_Text.moveCursor(QtGui.QTextCursor.Start) + self.finalizeSetupWin.show() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def finalizeSetup_Continue(self): + + #delete UI + self.finalizeSetup_Cancel() + + #toggle tab visibility + self.mainUI.toolModeStack.setCurrentIndex(1) + + #update network node with state change + if not cmds.objExists("ART_RIG_ROOT.state"): + cmds.addAttr("ART_RIG_ROOT", ln = "state", keyable = False) + cmds.setAttr("ART_RIG_ROOT.state", 1) + + #build bind skeleton + riggingUtils.buildSkeleton() + + #hide joint mover and lock + lockNodes = cmds.listRelatives("JointMover", children = True) + for node in lockNodes: + cmds.setAttr(node + ".v", 0, lock = True) + + #lock nodes + cmds.select("JointMover", hi = True) + jmNodes = cmds.ls(sl = True) + for node in jmNodes: + cmds.lockNode(node, lock = True) + + #clear selection + cmds.select(clear = True) + + #launch weight wizard + import ART_WeightWizard as aww + reload(aww) + aww.run(self.mainUI) + + #remove outliner scriptJobs + for job in self.mainUI.scriptJobs: + try: + cmds.scriptJob(kill = job, force = True) + print "killed job :" + str(job) + except: + pass + + #weight table scriptJob + self.mainUI.scriptJobs.append(self.skinToolsInst.weightTable_scriptJob()) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def finalizeSetup_Cancel(self): + + if cmds.window("ART_finalizeSetupWin", exists = True): + cmds.deleteUI("ART_finalizeSetupWin", wnd = True) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def finalizeSetup_Help(self): + print "Not implemented yet. This will need to link to documentation online." diff --git a/Core/Scripts/Interfaces/ART_Help.py b/Core/Scripts/Interfaces/ART_Help.py new file mode 100644 index 0000000..5e4ef84 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_Help.py @@ -0,0 +1,120 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import os +import System.utils as utils + +#Original Author: Jeremy Ernst + + +class ART_HelpMovie(): + def __init__(self, mainUI, moviePath): + #Original Author: Jeremy Ernst + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.projectPath = settings.value("projectPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + #images + self.imageBkgrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/toolbar_background.png")) + self.imageBtnBkrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/blue_field_background.png")) + self.frameBackground = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/field_background.png")) + + #build the UI + if cmds.window("ART_HelpMovieWin", exists = True): + cmds.deleteUI("ART_HelpMovieWin", wnd = True) + + self.buildHelpMovieUI(moviePath) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildHelpMovieUI(self, moviePath): + #Original Author: Jeremy Ernst + + #create the main window + self.mainWin = QtWidgets.QMainWindow(self.mainUI) + self.mainWin.setStyleSheet("background-color: rgb(0, 0, 0);, color: rgb(0,0,0);") + self.mainWin.setMinimumSize(660,520) + self.mainWin.setMaximumSize(660,520) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + #create the qFrame so we can have a background + self.frame = QtWidgets.QFrame(self.mainWidget) + self.frame.setStyleSheet("background-color: rgb(0,0,0);") + self.frame.setMinimumSize(660,520) + self.frame.setMaximumSize(660,520) + + #set qt object name + self.mainWin.setObjectName("ART_HelpMovieWin") + self.mainWin.setWindowTitle("Help") + + #font + headerFont = QtGui.QFont() + headerFont.setPointSize(10) + headerFont.setBold(True) + + #create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.frame) + + #set up the screen + self.movie_screen = QtWidgets.QLabel() + + # expand and center the label + self.movie_screen.setAlignment(QtCore.Qt.AlignCenter) + self.layout.addWidget(self.movie_screen) + + + #buttons and button layout + self.buttonlayout = QtWidgets.QHBoxLayout() + self.layout.addLayout(self.buttonlayout) + + spacer = QtWidgets.QSpacerItem(60,0) + self.buttonlayout.addSpacerItem(spacer) + + self.playBtn = QtWidgets.QPushButton("Close") + self.buttonlayout.addWidget(self.playBtn) + self.playBtn.clicked.connect(partial(self.close)) + self.playBtn.setStyleSheet("background-image: url(" + self.imageBtnBkrd + ");background-color: rgb(25, 175, 255);") + self.playBtn.setMinimumHeight(40) + self.playBtn.setMaximumHeight(40) + self.playBtn.setFont(headerFont) + + spacer = QtWidgets.QSpacerItem(60,0) + self.buttonlayout.addSpacerItem(spacer) + + + #set movie from file path + self.movie = QtGui.QMovie(moviePath, QtCore.QByteArray()) + self.movie.setCacheMode(QtGui.QMovie.CacheAll) + self.movie.setSpeed(100) + self.movie_screen.setMovie(self.movie) + + self.movie.start() + + + #show + self.mainWin.show() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def close(self): + #Original Author: Jeremy Ernst + + if cmds.window("ART_HelpMovieWin", exists = True): + cmds.deleteUI("ART_HelpMovieWin", wnd = True) +
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/ART_ImportMotionUI.py b/Core/Scripts/Interfaces/ART_ImportMotionUI.py new file mode 100644 index 0000000..42650f7 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ImportMotionUI.py @@ -0,0 +1,621 @@ +''' +Created on Aug 27, 2015 + +@author: jeremy.ernst +''' + + +#import statements +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import maya.mel as mel +import os, subprocess, sys, json +import System.utils as utils +import System.interfaceUtils as interfaceUtils + + + + + +class ART_ImportMotion(object): + def __init__(self, animPickerUI, parent = None): + + super(ART_ImportMotion, self).__init__() + + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + + self.pickerUI = animPickerUI + + #write out qss based on user settings + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir,sheet)) + + #build the UI + self.buildUI() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + + + if cmds.window("pyART_ImportMotionWIN", exists = True): + cmds.deleteUI("pyART_ImportMotionWIN", wnd = True) + + #create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + #create the mainLayout + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + + self.mainWin.setStyleSheet(self.style) + + + self.mainWin.setMinimumSize(QtCore.QSize( 600, 350 )) + self.mainWin.setMaximumSize(QtCore.QSize( 600, 350 )) + self.mainWin.resize(600, 350) + + + #set qt object name + self.mainWin.setObjectName("pyART_ImportMotionWIN") + self.mainWin.setWindowTitle("Import Motion") + + + #tabs + self.importTabs = QtWidgets.QTabWidget() + self.layout.addWidget(self.importTabs) + + #style sheet + stylesheet = """ + QTabBar::tab + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + width: 100px; + padding-left: -10px; + } + QTabBar::tab:selected + { + background-color: rgb(14,100,143); + border: 2px solid black; + } + QTabBar::tab:hover + { + background: rgb(19,132,183); + } + QTabBar::tab:!selected + { + margin-top: 5px; + } + QTabWidget::pane + { + border-top: 2px solid rgb(19,132,183); + border-left: 2px solid rgb(19,132,183); + border-right: 2px solid rgb(19,132,183); + border-bottom: 2px solid rgb(19,132,183); + } + """ + + self.importTabs.setStyleSheet(stylesheet) + + #FBX Tab + self.fbxImportTab = QtWidgets.QWidget() + self.importTabs.addTab(self.fbxImportTab, "FBX") + + #Anim Curve Tab + self.animImportTab = QtWidgets.QWidget() + self.importTabs.addTab(self.animImportTab, "Animation") + + + + #======================================================================= + #======================================================================= + #======================================================================= + #======================================================================= + # #FBX TAB + #======================================================================= + #======================================================================= + #======================================================================= + #======================================================================= + + #horizontal layout + self.fbxMainLayout = QtWidgets.QHBoxLayout(self.fbxImportTab) + + #LEFT SIDE + + #module list widget + self.fbxModuleList = QtWidgets.QListWidget() + self.fbxMainLayout.addWidget(self.fbxModuleList) + self.fbxModuleList.setMinimumSize(QtCore.QSize(300, 280)) + self.fbxModuleList.setMaximumSize(QtCore.QSize(300,280)) + self.fbxModuleList.setSpacing(15) + self.fbxModuleList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + + + + #RIGHT SIDE + + self.fbxRightLayout = QtWidgets.QVBoxLayout() + self.fbxMainLayout.addLayout(self.fbxRightLayout) + + self.fbxCharacterCombo = QtWidgets.QComboBox() + self.fbxRightLayout.addWidget(self.fbxCharacterCombo) + self.fbxCharacterCombo.setMinimumSize(QtCore.QSize(250, 50)) + self.fbxCharacterCombo.setMaximumSize(QtCore.QSize(250, 50)) + self.fbxCharacterCombo.setIconSize(QtCore.QSize(45,45)) + self.fbxCharacterCombo.currentIndexChanged.connect(partial(self.findCharacterModules)) + + + self.fbxPathLayout = QtWidgets.QHBoxLayout() + self.fbxRightLayout.addLayout(self.fbxPathLayout) + + self.fbxFilePath = QtWidgets.QLineEdit() + self.fbxFilePath.setMinimumWidth(210) + self.fbxFilePath.setMaximumWidth(210) + self.fbxPathLayout.addWidget(self.fbxFilePath) + self.fbxFilePath.setPlaceholderText("fbx file..") + + browseBtn = QtWidgets.QPushButton() + browseBtn.setMinimumSize(25,25) + browseBtn.setMaximumSize(25,25) + self.fbxPathLayout.addWidget(browseBtn) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/fileBrowse.png")) + browseBtn.setIconSize(QtCore.QSize(25,25)) + browseBtn.setIcon(icon) + browseBtn.clicked.connect(self.fbxFileBrowse) + + + self.frameOffsetLayout = QtWidgets.QHBoxLayout() + self.fbxRightLayout.addLayout(self.frameOffsetLayout) + + frameOffset = QtWidgets.QLabel("Frame Offset:") + frameOffset.setStyleSheet("background: transparent; font: bold;") + self.frameOffsetLayout.addWidget(frameOffset) + + + self.frameOffsetField = QtWidgets.QSpinBox() + self.frameOffsetField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.frameOffsetField.setRange(-1000, 10000) + self.frameOffsetLayout.addWidget(self.frameOffsetField) + + + #option to strip namespace + self.stripNamespace = QtWidgets.QCheckBox("Strip Incoming Namespace") + self.stripNamespace.setToolTip("If the incoming FBX has a namespace, checking this\noption will strip that namespace upon import") + self.stripNamespace.setChecked(True) + self.fbxRightLayout.addWidget(self.stripNamespace) + + + #Save/Load Settings + saveLoadLayout = QtWidgets.QHBoxLayout() + self.fbxRightLayout.addLayout(saveLoadLayout) + + saveSettingsBtn = QtWidgets.QPushButton("Save Settings") + saveSettingsBtn.setMinimumSize(120, 30) + saveSettingsBtn.setMaximumSize(120, 30) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/save.png")) + saveSettingsBtn.setIconSize(QtCore.QSize(25,25)) + saveSettingsBtn.setIcon(icon) + saveLoadLayout.addWidget(saveSettingsBtn) + saveSettingsBtn.setObjectName("blueButton") + saveSettingsBtn.setToolTip("Save out module import settings") + saveSettingsBtn.clicked.connect(self.saveSettings) + + + loadSettingsBtn = QtWidgets.QPushButton("Load Settings") + loadSettingsBtn.setMinimumSize(120, 30) + loadSettingsBtn.setMaximumSize(120, 30) + icon = QtGui.QIcon(utils.returnNicePath(self.iconsPath, "System/load.png")) + loadSettingsBtn.setIconSize(QtCore.QSize(25,25)) + loadSettingsBtn.setIcon(icon) + saveLoadLayout.addWidget(loadSettingsBtn) + loadSettingsBtn.setObjectName("blueButton") + loadSettingsBtn.setToolTip("Load and set module import settings") + loadSettingsBtn.clicked.connect(self.loadSettings) + + #SPACER! + self.fbxRightLayout.addSpacerItem(QtWidgets.QSpacerItem(0,0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + + #Button + self.importFBXbutton = QtWidgets.QPushButton("Import") + self.fbxRightLayout.addWidget(self.importFBXbutton) + self.importFBXbutton.setObjectName("blueButton") + self.importFBXbutton.setMinimumHeight(50) + self.importFBXbutton.clicked.connect(self.fbxImport) + + + #show window + self.mainWin.show() + + + #populate UI + self.findCharacters() + self.findCharacterModules() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacters(self): + + self.characterInfo = [] + + allNodes= cmds.ls(type = "network") + characterNodes = [] + for node in allNodes: + attrs = cmds.listAttr(node) + if "rigModules" in attrs: + characterNodes.append(node) + + #go through each node, find the character name, the namespace on the node, and the picker attribute + for node in characterNodes: + try: + namespace = cmds.getAttr(node + ".namespace") + except: + namespace = cmds.getAttr(node + ".name") + + + #add the icon found on the node's icon path attribute to the tab + iconPath = cmds.getAttr(node + ".iconPath") + iconPath = utils.returnNicePath(self.projectPath, iconPath) + icon = QtGui.QIcon(iconPath) + + + self.fbxCharacterCombo.addItem(icon, namespace) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacterModules(self, *args): + + self.fbxModuleList.clear() + + #current character + selectedChar = self.fbxCharacterCombo.currentText() + + #get rig modules + if cmds.objExists(selectedChar + ":" + "ART_RIG_ROOT"): + modules = cmds.listConnections(selectedChar + ":" + "ART_RIG_ROOT.rigModules") + + for module in modules: + niceName = cmds.getAttr(module + ".moduleName") + moduleType = cmds.getAttr(module + ".moduleType") + + + #create widget + item = QtWidgets.QListWidgetItem() + + widgetItem = QtWidgets.QGroupBox() + widgetItem.setMinimumHeight(40) + widgetItem.setProperty("module", module) + widgetItem.setObjectName("light") + + layout = QtWidgets.QHBoxLayout(widgetItem) + label = QtWidgets.QLabel(niceName) + label.setStyleSheet("background: transparent; font: bold;") + layout.addWidget(label) + + comboBox = QtWidgets.QComboBox() + layout.addWidget(comboBox) + + #add items to combo box bases on module class var + mod = __import__("RigModules." + moduleType, {}, {}, [moduleType]) + fbxOptions = mod.fbxImport + + for each in fbxOptions: + comboBox.addItem(each) + + comboBox.setCurrentIndex(1) + + self.fbxModuleList.addItem(item) + self.fbxModuleList.setItemWidget(item, widgetItem) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbxFileBrowse(self): + + settings = QtCore.QSettings("Epic Games", "ARTv2") + path = settings.value("ImportPath") + if path == None: + path = self.projectPath + + #see if export node exists, and if it does, see if there is an existing export path + try: + path = cmds.fileDialog2(fm = 1, okc = "Import FBX", dir = path, ff = "*.fbx") + nicePath = utils.returnFriendlyPath(path[0]) + + self.fbxFilePath.setText(nicePath) + settings.setValue("ImportPath", nicePath) + + except: + pass + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def fbxImport(self): + + + try: + #Maya 2015 has one click dependency on FBX. super annoying + cmds.loadPlugin("OneClick.mll") + except: + pass + + + #get the file path from the UI + filePath = self.fbxFilePath.text() + + if not os.path.exists(filePath): + cmds.warning("No such file exists") + return + + + #stripping namespace + if self.stripNamespace.isChecked(): + #open maya standalone + mayaPath = None + for path in sys.path: + if path.find("bin") != -1: + if path.find("bin" + os.sep) == -1: + mayaPath = utils.returnFriendlyPath(os.path.join(path, "mayapy.exe")) + + #error checking + if mayaPath == None: + try: + msg = interfaceUtils.DialogMessage("Error", "Unable to locate mayapy.exe", [], 0) + msg.show() + except: + cmds.warning("Unable to locate mayapy.exe.") + return + + scriptPath = utils.returnNicePath(self.scriptPath, "System/ART_StripFbxNamespace.py") + + #run a subprocess, opening mayapy/mayastandlone, running our stripNameSpace script + maya = subprocess.Popen(mayaPath + ' ' + scriptPath + ' ' + filePath, stdout = subprocess.PIPE, stderr = subprocess.PIPE) + out = maya.stdout.read() + err = maya.stderr.read() + + print out + + + + #get the current character + character = self.fbxCharacterCombo.currentText() + + #duplicate the character's root + if cmds.objExists("root"): + cmds.warning("There is already a skeleton in the scene with the name \"root\". Aborting") + return + + newSkeleton = cmds.duplicate(character + ":root") + cmds.select(newSkeleton) + cmds.delete(constraints = True) + + #go through each module in list, find import method, and setup constraints accordingly + moduleItems = [] + for i in range(self.fbxModuleList.count()): + item = self.fbxModuleList.item(i) + itemWidget = self.fbxModuleList.itemWidget(item) + itemModule = itemWidget.property("module") + + children = itemWidget.children() + for child in children: + if type(child) == QtWidgets.QComboBox: + importMethod = child.currentText() + moduleItems.append([itemModule, importMethod]) + + controls = [] + postModules = [] + + #setup the constraints + for each in moduleItems: + #get inst + modType = cmds.getAttr(each[0] + ".moduleType") + modName = cmds.getAttr(each[0] + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(mod) + + #list of modules that have post bake operations needed + specialModules = ["ART_Leg_Standard"] + + + + #get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + #find the instance of that module + moduleInst = moduleClass(self, modName) + + #set namespace for instance + moduleInst.namespace = character + ":" + + #run the module's pre import function + moduleInst.importFBX_pre(each[1], character) + + if modType in specialModules: + postModules.append([each[1], character,moduleInst]) + + returnControls = moduleInst.importFBX(each[1], character) + if returnControls != None: + controls.extend(returnControls) + + + #ensure that the scene is in 30fps + cmds.currentUnit(time = 'ntsc') + cmds.playbackOptions(min = 0, max = 100, animationStartTime = 0, animationEndTime = 100) + cmds.currentTime(0) + + #import the FBX file + string = "FBXImportMode -v \"exmerge\";" + string += "FBXImport -file \"" + filePath + "\"" + string += "FBXImportFillTimeline -v true" + mel.eval(string) + + + #ensure we're on the base layer + animLayers = cmds.ls(type = "animLayer") + if animLayers != []: + for layer in animLayers: + cmds.animLayer(layer, edit = True, selected = False) + cmds.animLayer("BaseAnimation", edit = True, selected = True, preferred = True) + + + #snap timeline to length of imported animation + cmds.select("root", hi = True) + firstFrame = cmds.findKeyframe(cmds.ls(sl = True), which = 'first') + lastFrame = cmds.findKeyframe(cmds.ls(sl = True),which = 'last') + if lastFrame == firstFrame: + lastFrame = lastFrame + 1 + + cmds.playbackOptions(min = firstFrame, max = lastFrame, animationStartTime = firstFrame, animationEndTime = lastFrame) + + #BAKE! + cmds.select(controls) + cmds.bakeResults(simulation = True, t = (firstFrame, lastFrame)) + + + #Post Modules: Modules that have post-bake operations needing to be done + for each in postModules: + method = each[0] + character = each[1] + inst = each[2] + + inst.importFBX_post(method, character) + + + #Clean up (delete duplicate skeleton) + cmds.delete("root") + + + #Look at frame offset, and offset animation based on that + frameOffset = self.frameOffsetField.value() + + cmds.select(controls) + cmds.keyframe(timeChange = frameOffset, r = True) + + firstFrame = cmds.findKeyframe( which = 'first') + lastFrame = cmds.findKeyframe(which = 'last') + cmds.playbackOptions(min = firstFrame, max = lastFrame, animationStartTime = firstFrame, animationEndTime = lastFrame) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def saveSettings(self): + + #loop through each of the modules and read the qComboBox value + moduleItems = [] + for i in range(self.fbxModuleList.count()): + item = self.fbxModuleList.item(i) + itemWidget = self.fbxModuleList.itemWidget(item) + itemModule = itemWidget.property("module") + + children = itemWidget.children() + for child in children: + if type(child) == QtWidgets.QComboBox: + importMethod = child.currentIndex() + moduleItems.append([itemModule, importMethod]) + + #save import settings in the settings folder + if not os.path.exists(os.path.join(self.toolsPath, "settings")): + os.makedirs(os.path.join(self.toolsPath, "settings")) + + if not os.path.exists(os.path.join(self.toolsPath, "settings" + os.sep + "importSettings")): + os.makedirs(os.path.join(self.toolsPath, "settings" + os.sep + "importSettings")) + + + #open a file browser dialog for user to name file + dialog = QtWidgets.QFileDialog(None, "Save", os.path.join(self.toolsPath, "settings" + os.sep + "importSettings")) + dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) + dialog.setDefaultSuffix("json") + dialog.exec_() + fileName = dialog.selectedFiles() + + #write file + f = open(fileName[0], 'w') + json.dump(moduleItems, f) + f.close() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def loadSettings(self): + + #open a file browser dialog for user to name file + dialog = QtWidgets.QFileDialog(None, "Open", os.path.join(self.toolsPath, "settings" + os.sep + "importSettings")) + dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) + dialog.setDefaultSuffix("json") + dialog.exec_() + fileName = dialog.selectedFiles() + + #open and read the file + f = open(fileName[0], 'r') + data = json.load(f) + f.close() + + #find items in UI + modules = {} + for i in range(self.fbxModuleList.count()): + item = self.fbxModuleList.item(i) + itemWidget = self.fbxModuleList.itemWidget(item) + itemModule = itemWidget.property("module") + + children = itemWidget.children() + for child in children: + if type(child) == QtWidgets.QComboBox: + modules[itemModule] = child + + #loop through data + keys = modules.keys() + for each in data: + if each[0] in keys: + comboBox = modules.get(each[0]) + comboBox.setCurrentIndex(each[1]) diff --git a/Core/Scripts/Interfaces/ART_ImportWeights.py b/Core/Scripts/Interfaces/ART_ImportWeights.py new file mode 100644 index 0000000..6895e5f --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ImportWeights.py @@ -0,0 +1,386 @@ +""" +Author: Jeremy Ernst + + This class builds the interface for importing skin weights from a .weights file. + +=============== +Class +=============== +""" + +import os +import traceback +from functools import partial + +import maya.cmds as cmds + +import System.riggingUtils as riggingUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_ImportSkinWeights(): + def __init__(self, mainUI): + """ + Instantiate the class, getting the settings from QSettings, then build the interface. + + :param mainUI: instance of the rig creator interface + """ + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + # build the UI + self.buildImportWeightsUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # USER INTERFACE + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildImportWeightsUI(self): + """ + Build the interface for importing skin weights. The interface will create an entry for every piece of selected + geometry. + + The interface will look like this: + + .. image:: /images/importWeights.png + + """ + if cmds.window("ART_importSkinWeightsUI", exists=True): + cmds.deleteUI("ART_importSkinWeightsUI", wnd=True) + + # launch a UI to get the name information + self.importSkinWeights_Win = QtWidgets.QMainWindow(self.mainUI) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.importSkinWeights_Win.setStyleSheet(self.style) + + # create the main widget + self.importSkinWeights_mainWidget = QtWidgets.QWidget() + self.importSkinWeights_Win.setCentralWidget(self.importSkinWeights_mainWidget) + + # set qt object name + self.importSkinWeights_Win.setObjectName("ART_importSkinWeightsUI") + self.importSkinWeights_Win.setWindowTitle("Import Skin Weights") + + # create the mainLayout for the ui + self.importSkinWeights_mainLayout = QtWidgets.QVBoxLayout(self.importSkinWeights_mainWidget) + self.importSkinWeights_mainLayout.setContentsMargins(5, 5, 5, 5) + + self.importSkinWeights_Win.resize(450, 400) + # self.importSkinWeights_Win.setSizePolicy(mainSizePolicy) + self.importSkinWeights_Win.setMinimumSize(QtCore.QSize(450, 400)) + self.importSkinWeights_Win.setMaximumSize(QtCore.QSize(450, 400)) + + # create the background image + self.importSkinWeights_frame = QtWidgets.QFrame() + self.importSkinWeights_mainLayout.addWidget(self.importSkinWeights_frame) + self.importSkinWeights_frame.setObjectName("dark") + + # create widgetLayout + self.importSkinWeights_widgetLayout = QtWidgets.QVBoxLayout(self.importSkinWeights_frame) + + # import skin weights method + # self.importSkinWeights_methodForm = QtWidgets.QHBoxLayout() + # self.importSkinWeights_widgetLayout.addLayout(self.importSkinWeights_methodForm) + # + # label = QtWidgets.QLabel("Import Method: ") + # label.setStyleSheet("background: transparent;") + # self.importSkinWeights_methodForm.addWidget(label) + + # self.importSkinWeights_importMethod = QtWidgets.QComboBox() + # self.importSkinWeights_methodForm.addWidget(self.importSkinWeights_importMethod) + # self.importSkinWeights_importMethod.addItem("Vertex Order") + # self.importSkinWeights_importMethod.addItem("World Position") + # self.importSkinWeights_importMethod.addItem("Local Position") + # self.importSkinWeights_importMethod.addItem("UV Position") + + # scroll area contents + self.importSkinWeights_scrollContents = QtWidgets.QFrame() + self.importSkinWeights_scrollContents.setObjectName("light") + + # Layout of Container Widget + self.importSkinWeights_VLayout = QtWidgets.QVBoxLayout() + self.importSkinWeights_VLayout.setSpacing(5) + + # find selected geometry and populate scroll area + self.importSkinWeights_populate() + + # add scrollArea for selected geo, skinFileName, and checkbox for importing + self.importSkinWeights_scrollLayout = QtWidgets.QScrollArea() + self.importSkinWeights_widgetLayout.addWidget(self.importSkinWeights_scrollLayout) + self.importSkinWeights_scrollLayout.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.importSkinWeights_scrollLayout.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.importSkinWeights_scrollLayout.setWidgetResizable(False) + self.importSkinWeights_scrollLayout.setWidget(self.importSkinWeights_scrollContents) + + # refresh and import button + font = QtGui.QFont() + font.setPointSize(8) + font.setBold(True) + + self.importSkinWeights_BtnLayout = QtWidgets.QHBoxLayout() + self.importSkinWeights_widgetLayout.addLayout(self.importSkinWeights_BtnLayout) + + self.importSkinWeights_RefreshBtn = QtWidgets.QPushButton("Refresh") + self.importSkinWeights_BtnLayout.addWidget(self.importSkinWeights_RefreshBtn) + self.importSkinWeights_RefreshBtn.setMinimumSize(QtCore.QSize(70, 50)) + self.importSkinWeights_RefreshBtn.setMaximumSize(QtCore.QSize(70, 50)) + self.importSkinWeights_RefreshBtn.setFont(font) + self.importSkinWeights_RefreshBtn.clicked.connect(partial(self.buildImportWeightsUI)) + self.importSkinWeights_RefreshBtn.setObjectName("blueButton") + + self.importSkinWeights_ImportBtn = QtWidgets.QPushButton("IMPORT WEIGHTS") + self.importSkinWeights_BtnLayout.addWidget(self.importSkinWeights_ImportBtn) + self.importSkinWeights_ImportBtn.setMinimumSize(QtCore.QSize(350, 50)) + self.importSkinWeights_ImportBtn.setMaximumSize(QtCore.QSize(350, 50)) + self.importSkinWeights_ImportBtn.setFont(font) + self.importSkinWeights_ImportBtn.clicked.connect(partial(self.importSkinWeights_DoImport)) + self.importSkinWeights_ImportBtn.setObjectName("blueButton") + + # lastly, progress bar + self.importSkinWeights_progBarTotal = QtWidgets.QProgressBar() + self.importSkinWeights_widgetLayout.addWidget(self.importSkinWeights_progBarTotal) + self.importSkinWeights_progBarTotal.setRange(0, self.importSkinWeights_VLayout.count() - 1) + self.importSkinWeights_progBarTotal.setValue(0) + + self.importSkinWeights_progBarCurrent = QtWidgets.QProgressBar() + self.importSkinWeights_widgetLayout.addWidget(self.importSkinWeights_progBarCurrent) + self.importSkinWeights_progBarCurrent.setRange(0, 100) + self.importSkinWeights_progBarCurrent.setValue(0) + + # show window + self.importSkinWeights_Win.show() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def importSkinWeights_populate(self): + """ + Populate the interface with an entry for each piece of selected geometry. Each entry will have the geometry + name and allow the user to point to the geometry's .weight file. + """ + + # get current selection + selection = cmds.ls(sl=True) + if len(selection) > 0: + + # Create headers + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + headerLayout = QtWidgets.QHBoxLayout() + self.importSkinWeights_VLayout.addLayout(headerLayout) + headerExport = QtWidgets.QLabel(" ") + headerExport.setStyleSheet("background: transparent;") + headerLayout.addWidget(headerExport) + + headerGeo = QtWidgets.QLabel("Mesh") + headerGeo.setStyleSheet("background: transparent;") + headerGeo.setMinimumSize(QtCore.QSize(180, 20)) + headerGeo.setMaximumSize(QtCore.QSize(180, 20)) + headerLayout.addWidget(headerGeo) + headerGeo.setFont(font) + + headerFileName = QtWidgets.QLabel("Weight File") + headerFileName.setStyleSheet("background: transparent;") + headerLayout.addWidget(headerFileName) + headerFileName.setMinimumSize(QtCore.QSize(180, 20)) + headerFileName.setMaximumSize(QtCore.QSize(180, 20)) + headerFileName.setFont(font) + + # get a list of weight files + weightFiles = [] + for root, subFolders, files in os.walk(self.toolsPath): + for file in files: + if file.rpartition(".")[2] == "weights": + fullPath = utils.returnFriendlyPath(os.path.join(root, file)) + + weightFiles.append(fullPath) + print weightFiles + # loop through selection, checking selection is valid and has skinCluster + for each in selection: + + try: + # get dagPath and shape and create a nice display name + dagPath = cmds.ls(each, long=True)[0] + shapeNode = cmds.listRelatives(dagPath, children=True) + nicename = each.rpartition("|")[2] + except Exception, e: + traceback.format_exc() + + try: + if cmds.nodeType(dagPath + "|" + shapeNode[0]) == "mesh": + # create HBoxLayout + layout = QtWidgets.QHBoxLayout() + layout.setSpacing(10) + self.importSkinWeights_VLayout.addLayout(layout) + + # create checkbox + checkBox = QtWidgets.QCheckBox() + layout.addWidget(checkBox) + checkBox.setChecked(True) + + # create non editable line edit + geoName = QtWidgets.QLabel(nicename + " : ") + geoName.setStyleSheet("background: transparent;") + geoName.setProperty("dag", dagPath) + layout.addWidget(geoName) + geoName.setMinimumSize(QtCore.QSize(100, 30)) + geoName.setMaximumSize(QtCore.QSize(100, 30)) + + # create editable line edit + skinFileName = QtWidgets.QLineEdit() + layout.addWidget(skinFileName) + skinFileName.setMinimumSize(QtCore.QSize(205, 30)) + skinFileName.setMaximumSize(QtCore.QSize(205, 30)) + + # try to find a matching weight file + for file in weightFiles: + compareString = file.rpartition("/")[2].partition(".")[0] + if nicename.lower() == compareString.lower(): + skinFileName.setText(file) + + # check if geometry has weights file associated already + if cmds.objExists(dagPath + ".weightFile"): + path = cmds.getAttr(dagPath + ".weightFile") + path = utils.returnFriendlyPath(path) + if os.path.exists(path): + skinFileName.setText(path) + + # browse button + browseBtn = QtWidgets.QPushButton() + layout.addWidget(browseBtn) + browseBtn.setMinimumSize(35, 35) + browseBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/fileBrowse.png")) + browseBtn.setIconSize(QtCore.QSize(30, 30)) + browseBtn.setIcon(icon) + browseBtn.clicked.connect(partial(self.importSkinWeights_fileBrowse, skinFileName)) + except Exception, e: + print traceback.format_exc() + + # add spacer + self.importSkinWeights_scrollContents.setLayout(self.importSkinWeights_VLayout) + + else: + label = QtWidgets.QLabel("No Geometry Selected For Import. Select Geometry and Relaunch.") + label.setAlignment(QtCore.Qt.AlignCenter) + self.importSkinWeights_VLayout.addWidget(label) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def importSkinWeights_fileBrowse(self, lineEdit): + """ + Open a file dialog that the user can use to browse to the .weight file, then set the text of the passed-in + line edit to be the path to the .weights file. + + :param lineEdit: QLineEdit to set path text to. + """ + + # Need support for defaulting to current project character was last published to and creating character + # skin weights folder + + try: + path = cmds.fileDialog2(fm=1, dir=self.toolsPath)[0] + nicePath = utils.returnFriendlyPath(path) + lineEdit.setText(nicePath) + except: + pass + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def importSkinWeights_DoImport(self): + """ + Gather the information from the interface and import the skin weights with that information. + + For each piece of geometry, get the associated .weight file on disk, then call on import_skin_weights + from riggingUtils, passing in the .weight file path and the mesh. + + """ + # which method? + # method = self.importSkinWeights_importMethod.currentText() + + # error report messages + errorMessages = [] + + # weight files + weightFiles = [] + + # find each lineEdit in the scrollArea and get the entered text + for i in range(self.importSkinWeights_VLayout.count()): + hboxLayout = self.importSkinWeights_VLayout.itemAt(i) + value, fileName, mesh = None, None, None + for x in range(hboxLayout.count()): + widget = hboxLayout.itemAt(x) + # go through each widget in the hboxLayout, and get values + if type(widget.widget()) == QtWidgets.QLabel: + geoName = widget.widget().text() + geoName = geoName.partition(" :")[0] + if cmds.objExists(geoName): + mesh = cmds.ls(geoName, long=True)[0] + + # see if the user wants to export the weights for this entry + if type(widget.widget()) == QtWidgets.QCheckBox: + value = widget.widget().isChecked() + + # get the fileName + if type(widget.widget()) == QtWidgets.QLineEdit: + fileName = widget.widget().text() + + # if the box is checked for import, do the import + if (value and mesh and fileName): + ############################### + # BEGIN WEIGHT IMPORT + + # try to load the given file + if os.path.exists(fileName): + weightFiles.append(fileName) + else: + cmds.error('ART_ImportWeights: Skin file does not exist: ' + fileName) + return False + if not mesh: + cmds.error('ART_ImportWeights: Mesh does not exist!') + return False + # update the total progress bar + incrementValue = self.importSkinWeights_progBarTotal.value() + 1 + self.importSkinWeights_progBarTotal.setValue(incrementValue) + + # create a skinWeights class with the skin file + if fileName: + print 'Loading skinWeights from ', str(fileName) + riggingUtils.import_skin_weights(fileName, mesh, True) + + # ask if user wants to delete weight files + file_string = "" + for file in weightFiles: + file_name = os.path.basename(file) + file_string += file_name + "\n" + + msgBox = QtWidgets.QMessageBox() + msgBox.setIcon(QtWidgets.QMessageBox.Warning) + msgBox.setText("Would you like to delete the following weight files?") + msgBox.setDetailedText(file_string) + msgBox.addButton("Yes", QtWidgets.QMessageBox.YesRole) + msgBox.addButton("No", QtWidgets.QMessageBox.NoRole) + ret = msgBox.exec_() + + if ret == 0: + for file in weightFiles: + try: + os.remove(file) + except: + cmds.warning("Unable to delete file: " + str(file)) + if ret == 1: + return diff --git a/Core/Scripts/Interfaces/ART_MatchOverRangeUI.py b/Core/Scripts/Interfaces/ART_MatchOverRangeUI.py new file mode 100644 index 0000000..a713ece --- /dev/null +++ b/Core/Scripts/Interfaces/ART_MatchOverRangeUI.py @@ -0,0 +1,287 @@ + +import os +from functools import partial + +import maya.cmds as cmds + +import System.interfaceUtils as interfaceUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_MatchOverRange(object): + def __init__(self, animPickerUI, parent=None): + + super(ART_MatchOverRange, self).__init__() + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.pickerUI = animPickerUI + + # write out qss based on user settings + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir, sheet)) + + # build the UI + self.buildUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + + if cmds.window("pyART_MatchOverRangeWIN", exists=True): + cmds.deleteUI("pyART_MatchOverRangeWIN", wnd=True) + + # create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + + # create the main widget + self.mainWidget = QtWidgets.QFrame() + self.mainWidget.setObjectName("dark") + self.mainWin.setCentralWidget(self.mainWidget) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + self.mainWin.setStyleSheet(self.style) + + # set window size + self.mainWin.setMinimumSize(QtCore.QSize(600, 350)) + self.mainWin.setMaximumSize(QtCore.QSize(600, 350)) + self.mainWin.resize(600, 350) + + # set qt object name + self.mainWin.setObjectName("pyART_MatchOverRangeWIN") + self.mainWin.setWindowTitle("Match Over Frame Range") + + # horizontal layout + self.mainLayout = QtWidgets.QHBoxLayout(self.mainWidget) + + # LEFT SIDE + # module list widget + self.moduleList = QtWidgets.QListWidget() + self.mainLayout.addWidget(self.moduleList) + # self.moduleList.setMinimumSize(QtCore.QSize(300, 280)) + # self.moduleList.setMaximumSize(QtCore.QSize(300, 280)) + self.moduleList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.moduleList.setSpacing(20) + + # RIGHT SIDE + self.rightLayout = QtWidgets.QVBoxLayout() + self.mainLayout.addLayout(self.rightLayout) + + self.rightLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 25, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + self.characterCombo = QtWidgets.QComboBox() + self.rightLayout.addWidget(self.characterCombo) + self.characterCombo.setMinimumHeight(50) + self.characterCombo.setMaximumHeight(50) + self.characterCombo.setIconSize(QtCore.QSize(50, 50)) + self.characterCombo.currentIndexChanged.connect(partial(self.findCharacterModules)) + + # frame ranges + + # SPACER! + self.rightLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 40, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + + self.rangeLayout = QtWidgets.QHBoxLayout() + self.rightLayout.addLayout(self.rangeLayout) + + label1 = QtWidgets.QLabel("Start:") + label1.setStyleSheet("background: transparent;") + self.rangeLayout.addWidget(label1) + + self.startFrame = QtWidgets.QSpinBox() + self.startFrame.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.rangeLayout.addWidget(self.startFrame) + self.startFrame.setRange(-10000, 10000) + + label2 = QtWidgets.QLabel("End:") + label2.setStyleSheet("background: transparent;") + self.rangeLayout.addWidget(label2) + + self.endFrame = QtWidgets.QSpinBox() + self.endFrame.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.rangeLayout.addWidget(self.endFrame) + self.endFrame.setRange(-10000, 10000) + + # SPACER! + self.rightLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + # Button + self.matchButton = QtWidgets.QPushButton("Match") + self.rightLayout.addWidget(self.matchButton) + self.matchButton.setObjectName("blueButton") + self.matchButton.setMinimumHeight(50) + self.matchButton.clicked.connect(self.match) + + # SPACER! + self.rightLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 25, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + + # show the window + self.mainWin.show() + + # populate UI + self.findCharacters() + self.findCharacterModules() + + startFrame = cmds.playbackOptions(q=True, min=True) + endFrame = cmds.playbackOptions(q=True, max=True) + + self.startFrame.setValue(startFrame) + self.endFrame.setValue(endFrame) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacters(self): + + allNodes = cmds.ls(type="network") + characterNodes = [] + for node in allNodes: + attrs = cmds.listAttr(node) + if "rigModules" in attrs: + characterNodes.append(node) + + # go through each node, find the character name, the namespace on the node, and the picker attribute + for node in characterNodes: + try: + namespace = cmds.getAttr(node + ".namespace") + except: + namespace = cmds.getAttr(node + ".name") + + # add the icon found on the node's icon path attribute to the tab + iconPath = cmds.getAttr(node + ".iconPath") + iconPath = utils.returnNicePath(self.projectPath, iconPath) + icon = QtGui.QIcon(iconPath) + + self.characterCombo.addItem(icon, namespace) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def findCharacterModules(self, *args): + + self.moduleList.clear() + + # current character + selectedChar = self.characterCombo.currentText() + + # get rig modules + if cmds.objExists(selectedChar + ":" + "ART_RIG_ROOT"): + modules = cmds.listConnections(selectedChar + ":" + "ART_RIG_ROOT.rigModules") + + for module in modules: + niceName = cmds.getAttr(module + ".moduleName") + moduleType = cmds.getAttr(module + ".moduleType") + + # create widget + item = QtWidgets.QListWidgetItem() + + widgetItem = QtWidgets.QGroupBox() + widgetItem.setMinimumHeight(50) + widgetItem.setProperty("module", module) + widgetItem.setObjectName("light") + + layout = QtWidgets.QHBoxLayout(widgetItem) + + checkBox = QtWidgets.QCheckBox(niceName) + checkBox.setChecked(False) + layout.addWidget(checkBox) + + comboBox = QtWidgets.QComboBox() + layout.addWidget(comboBox) + + # add items to combo box bases on module class var + mod = __import__("RigModules." + moduleType, {}, {}, [moduleType]) + matchData = mod.matchData + + if matchData[0] is True: + for each in matchData[1]: + comboBox.addItem(each) + + comboBox.setCurrentIndex(1) + + self.moduleList.addItem(item) + self.moduleList.setItemWidget(item, widgetItem) + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def match(self): + + # get the current character + character = self.characterCombo.currentText() + + # go through each module in list, find import method, and setup constraints accordingly + moduleItems = [] + for i in range(self.moduleList.count()): + item = self.moduleList.item(i) + itemWidget = self.moduleList.itemWidget(item) + itemModule = itemWidget.property("module") + + children = itemWidget.children() + for child in children: + + if type(child) == QtWidgets.QCheckBox: + value = child.isChecked() + if value == False: + break + + if type(child) == QtWidgets.QComboBox: + matchMethod = child.currentText() + moduleItems.append([itemModule, matchMethod]) + + # get frame range + start = self.startFrame.value() + end = self.endFrame.value() + + # loop through frame range, calling each module's match function given the match method + if len(moduleItems) > 0: + for i in range(start, end + 1): + cmds.currentTime(i) + for each in moduleItems: + # get inst + modType = cmds.getAttr(each[0] + ".moduleType") + modName = cmds.getAttr(each[0] + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(mod) + + # get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + # find the instance of that module + moduleInst = moduleClass(self, modName) + + # set namespace for instance + moduleInst.namespace = character + ":" + + # call on module's match function (method, checkbox, match over range) + moduleInst.switchMode(each[1], None, True) diff --git a/Core/Scripts/Interfaces/ART_ModuleStatus.py b/Core/Scripts/Interfaces/ART_ModuleStatus.py new file mode 100644 index 0000000..0f305e7 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ModuleStatus.py @@ -0,0 +1,156 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +import maya.cmds as cmds +import System.utils as utils + + +windowTitle = "Module Stats" +windowObject = "pyArtModStatusUi" + + + +class ART_ModStatusWin(QtWidgets.QMainWindow): + + def __init__(self, parent = None): + + + super(ART_ModStatusWin, self).__init__(parent) + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + style = f.read() + f.close() + + self.setStyleSheet(style) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + #set qt object name + self.setObjectName(windowObject) + self.setWindowTitle(windowTitle) + + #create the mainLayout for the rig creator UI + self.mainLayout = QtWidgets.QHBoxLayout(self.mainWidget) + self.mainLayout.setContentsMargins(0, 0, 0, 0) + + self.setMinimumSize(QtCore.QSize(300, 400)) + self.setMaximumSize(QtCore.QSize(300, 600)) + self.resize(QtCore.QSize(300, 400)) + + + #create the background + self.frame = QtWidgets.QFrame() + self.frame.setObjectName("mid") + self.mainLayout.addWidget(self.frame) + + + #create the widget layout + self.widgetLayout = QtWidgets.QVBoxLayout(self.frame) + + #create the table widget + self.modTable = QtWidgets.QTableWidget() + self.modTable.setObjectName("mid") + self.widgetLayout.addWidget(self.modTable) + self.modTable.setColumnCount(3) + self.modTable.setHorizontalHeaderLabels(["Module", "Pinned", "Aiming"]) + self.modTable.setColumnWidth(0, 100) + self.modTable.setColumnWidth(1, 65) + self.modTable.setColumnWidth(2, 65) + + #populate the table widget + self.populateTable() + + + #add refresh button + self.refreshButton = QtWidgets.QPushButton("Refresh") + self.widgetLayout.addWidget(self.refreshButton) + self.refreshButton.setObjectName("blueButton") + self.refreshButton.clicked.connect(self.refresh) + + + self.show() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateTable(self): + + pixmap = QtGui.QPixmap(20, 20) + pixmap.fill(QtGui.QColor(0, 255, 0)) + iconOn = QtGui.QIcon(pixmap) + + pixmapOff = QtGui.QPixmap(20, 20) + pixmapOff.fill(QtGui.QColor(255, 0, 0)) + iconOff = QtGui.QIcon(pixmapOff) + + modules = utils.returnRigModules() + self.modTable.setRowCount(100 + len(modules)) + counter = 0 + for module in modules: + + aimState = False + pinState = False + + #get module name + moduleName = cmds.getAttr(module + ".moduleName") + + if cmds.objExists(module + ".aimMode"): + aimState = cmds.getAttr(module + ".aimMode") + if cmds.objExists(module + ".pinned"): + pinState = cmds.getAttr(module + ".pinned") + + moduleItem = QtWidgets.QTableWidgetItem(moduleName) + self.modTable.setItem(counter, 0, moduleItem) + + if aimState: + lockItem = QtWidgets.QTableWidgetItem(iconOn, "") + else: + lockItem = QtWidgets.QTableWidgetItem(iconOff, "") + + self.modTable.setItem(counter, 2, lockItem) + + + if pinState: + pinItem = QtWidgets.QTableWidgetItem(iconOn, "") + else: + pinItem = QtWidgets.QTableWidgetItem(iconOff, "") + + self.modTable.setItem(counter, 1, pinItem) + + counter += 1 + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def refresh(self): + + while self.modTable.rowCount() > 0: + self.modTable.removeRow(0) + + self.populateTable() + + + + +def run(parent): + + if cmds.window("pyArtModStatusUi", exists = True): + cmds.deleteUI("pyArtModStatusUi", wnd = True) + + win = ART_ModStatusWin(parent) + win.show() + diff --git a/Core/Scripts/Interfaces/ART_MoveInfluences.py b/Core/Scripts/Interfaces/ART_MoveInfluences.py new file mode 100644 index 0000000..bc921e4 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_MoveInfluences.py @@ -0,0 +1,177 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import System.utils as utils + + + +class ART_MoveInfluences(): + def __init__(self, mainUI): + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + self.skinCluster = None + + #build the UI + self.buildMoveInfsUI() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildMoveInfsUI(self): + + if cmds.window("ART_MoveInfluencesWin", exists = True): + cmds.deleteUI("ART_MoveInfluencesWin", wnd = True) + + #launch a UI to get the name information + self.moveInfsWin = QtWidgets.QMainWindow(self.mainUI) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.moveInfsWin.setStyleSheet(self.style) + + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.moveInfsWin_mainWidget = QtWidgets.QWidget() + self.moveInfsWin.setCentralWidget(self.moveInfsWin_mainWidget) + + #set qt object name + self.moveInfsWin.setObjectName("ART_MoveInfluencesWin") + self.moveInfsWin.setWindowTitle("Move Influences") + + #create the mainLayout for the rig creator UI + self.moveInfsWin_mainLayout = QtWidgets.QVBoxLayout(self.moveInfsWin_mainWidget) + self.moveInfsWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.moveInfsWin.resize(300, 100) + self.moveInfsWin.setSizePolicy(mainSizePolicy) + self.moveInfsWin.setMinimumSize(QtCore.QSize( 300, 100 )) + self.moveInfsWin.setMaximumSize(QtCore.QSize( 300, 100 )) + + #create the background image + self.moveInfsWin_frame = QtWidgets.QFrame() + self.moveInfsWin_mainLayout.addWidget(self.moveInfsWin_frame) + + #layout for the widgets + self.moveInfsWin_widgetLayout = QtWidgets.QVBoxLayout(self.moveInfsWin_frame) + + #layout for the combo boxes + self.moveInfsWin_comboBoxLayout = QtWidgets.QHBoxLayout() + self.moveInfsWin_widgetLayout.addLayout(self.moveInfsWin_comboBoxLayout) + + #combo boxes + self.moveFromComboBox = QtWidgets.QComboBox() + self.moveInfsWin_comboBoxLayout.addWidget(self.moveFromComboBox) + + label = QtWidgets.QLabel(" --------> ") + label.setMaximumWidth(40) + label.setAlignment(QtCore.Qt.AlignCenter) + self.moveInfsWin_comboBoxLayout.addWidget(label) + + self.moveToComboBox = QtWidgets.QComboBox() + self.moveInfsWin_comboBoxLayout.addWidget(self.moveToComboBox) + + #process button + self.moveInfsGoBtn = QtWidgets.QPushButton("Move Influences") + self.moveInfsGoBtn.setObjectName("blueButton") + self.moveInfsWin_widgetLayout.addWidget(self.moveInfsGoBtn) + self.moveInfsGoBtn.setMinimumHeight(40) + self.moveInfsGoBtn.clicked.connect(partial(self.moveInfluences)) + + #populate the lists + status = self.findInfluencesOnSelection() + if not status: + return + + #show the window + self.moveInfsWin.show() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findInfluencesOnSelection(self): + + #find the selected mesh + selection = cmds.ls(sl = True) + + influences = [] + + #check if valid selection + if len(selection) >= 1: + if selection[0].find(".vtx") != -1: + mesh = selection[0].partition(".vtx")[0] + + if cmds.nodeType(mesh) == "transform": + + #check for skinCluster + skinClusters = cmds.ls(type = 'skinCluster') + + #go through each found skin cluster, and if we find a skin cluster whose geometry matches our selection, get influences + for cluster in skinClusters: + geometry = cmds.skinCluster(cluster, q = True, g = True)[0] + geoTransform = cmds.listRelatives(geometry, parent = True)[0] + if geoTransform == mesh: + self.skinCluster = cluster + influences = cmds.skinCluster(cluster, q= True, inf = True) + + else: + cmds.warning("please select the vertices you want to operate on.") + return False + + #populate combo boxes + for inf in influences: + self.moveFromComboBox.addItem(inf) + self.moveToComboBox.addItem(inf) + + return True +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def moveInfluences(self): + + + #make sure there is still a valid selection + selection = cmds.ls(sl = True) + valid = False + if len(selection) >= 1: + if selection[0].find(".vtx") != -1: + valid = True + + if valid: + #make sure the combo boxes don't have the same values + moveFrom = self.moveFromComboBox.currentText() + moveTo = self.moveToComboBox.currentText() + + if moveFrom == moveTo: + cmds.warning("Target influence cannot be the same as the source influence.") + + else: + cmds.skinPercent(self.skinCluster, tmw = [moveFrom, moveTo]) + cmds.deleteUI("ART_MoveInfluencesWin", wnd = True) + selection = cmds.ls(sl = True, flatten = True) + cmds.select(clear = True) + cmds.select(selection) + + if not valid: + cmds.warning("No vertices selected to operate on.") + diff --git a/Core/Scripts/Interfaces/ART_MovePickerToTabUI.py b/Core/Scripts/Interfaces/ART_MovePickerToTabUI.py new file mode 100644 index 0000000..4831046 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_MovePickerToTabUI.py @@ -0,0 +1,244 @@ +''' +Created on Aug 21, 2015 + +@author: jeremy.ernst +''' + +#import statements +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +import maya.cmds as cmds +import System.utils as utils + + +class ART_MovePickerToTab(object): + def __init__(self, animPickerUI, modulesToAdd, parent = None): + + super(ART_MovePickerToTab, self).__init__() + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.pickerUI = animPickerUI + self.modulesToAdd = modulesToAdd + + #assign close event + self.closeEvent = self.closeWin + + #build the UI + self.buildUI() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + + + if cmds.window("pyART_movePickerToTabWIN", exists = True): + cmds.deleteUI("pyART_movePickerToTabWIN", wnd = True) + + #create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + #create the mainLayout + self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) + self.layout = QtWidgets.QHBoxLayout() + self.mainLayout.addLayout(self.layout) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + + self.mainWin.setStyleSheet(self.style) + + self.mainWin.setMinimumSize(QtCore.QSize( 400, 400 )) + self.mainWin.setMaximumSize(QtCore.QSize( 400, 400 )) + self.mainWin.resize(400, 400) + + + #set qt object name + self.mainWin.setObjectName("pyART_movePickerToTabWIN") + self.mainWin.setWindowTitle("Move Picker") + + + #create 2 columns + self.column1 = QtWidgets.QVBoxLayout() + self.layout.addLayout(self.column1) + + self.column2 = QtWidgets.QVBoxLayout() + self.layout.addLayout(self.column2) + + #create left side list widget, which will house the picker items + self.pickerItemsList = QtWidgets.QListWidget() + self.column1.addWidget(self.pickerItemsList) + self.pickerItemsList.setMinimumSize(180, 300) + self.pickerItemsList.setMaximumSize(180, 300) + + + #get the current tab index and the widget + index = self.pickerUI.characterTabs.currentIndex() + widget = self.pickerUI.characterTabs.widget(index) + + #get the tab text + character = self.pickerUI.characterTabs.tabToolTip(index) + + + #find character nodes in the scene, and compare namespace to selected tab + characterMods = utils.returnCharacterModules() + nodeNamespace = "" + + for each in characterMods: + if cmds.objExists(each + ".namespace"): + namespace = cmds.getAttr(each + ".namespace") + if namespace == character: + nodeNamespace = namespace + ":" + + + for module in self.modulesToAdd: + if module[2] == None: + modName = cmds.getAttr(nodeNamespace + module[0] + ".moduleName") + else: + modName = module[2] + + qlistItem = QtWidgets.QListWidgetItem(modName) + qlistItem.setData(QtCore.Qt.UserRole, module[1]) + self.pickerItemsList.addItem(qlistItem) + + + #create right side list widget, which will house the available tabs + self.tabList = QtWidgets.QListWidget() + self.column2.addWidget(self.tabList) + self.tabList.setMinimumSize(180, 300) + self.tabList.setMaximumSize(180, 300) + + tabs = self.findTabs(False) + for tab in tabs: + qlistItem = QtWidgets.QListWidgetItem(tab[0]) + qlistItem.setData(QtCore.Qt.UserRole, tab[1]) + self.tabList.addItem(qlistItem) + + + #create button for move selected picker to selected tab + self.movePickerBtn = QtWidgets.QPushButton("Move Selected Picker To Selected Tab") + self.mainLayout.addWidget(self.movePickerBtn) + self.movePickerBtn.setObjectName("blueButton") + self.movePickerBtn.setMinimumHeight(50) + self.movePickerBtn.setMaximumHeight(50) + self.movePickerBtn.clicked.connect(self.moveToTab) + + + #show ui + self.mainWin.show() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findTabs(self, moving, newTab = None): + + #get the current tab index and the widget + index = self.pickerUI.characterTabs.currentIndex() + widget = self.pickerUI.characterTabs.widget(index) + tabs = [] + + #get the children of the current tab widget + children = widget.children() + for child in children: + + #if we find a tab widget, search for the gfxScene + if type(child) == QtWidgets.QTabWidget: + tab = child + + for i in range(tab.count()): + tabName = tab.tabText(i) + tabs.append([tabName, i]) + + if moving == True: + tab.setCurrentIndex(newTab) + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + canvasChildren = canvasWidget.children() + + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + if moving: + return scene + else: + return tabs + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def moveToTab(self): + + selectedItem = self.pickerItemsList.currentItem() + selectedTab = self.tabList.currentItem() + + try: + newTab = selectedTab.data(QtCore.Qt.UserRole) + except: + cmds.warning("Please Select a Tab from the list.") + return + + #get selected tab's scene + scene = self.findTabs(True, newTab) + + #get data from selectedItem + pickerItem = selectedItem.data(QtCore.Qt.UserRole) + try: + parentXform = pickerItem.parentItem().transform() + except: + parentXform = pickerItem.transform() + + #add to scene + scene.addItem(pickerItem) + + + #======================================================================= + # #mirror if needed + #======================================================================= + if parentXform.m11() == -1: + pickerItem.setTransformOriginPoint(pickerItem.boundingRect().center()) + pickerItem.setTransform(QtGui.QTransform(-1.0, 0.0, 0.0, 1.0, scene.sceneRect().width(), 0.0)) + pickerItem.setTransformOriginPoint(pickerItem.boundingRect().center()) + + + children = pickerItem.childItems() + for child in children: + if type(child) == QtWidgets.QGraphicsSimpleTextItem: + child.setTransformOriginPoint(child.boundingRect().center()) + child.setTransform(QtGui.QTransform(-1.0, 0.0, 0.0, 1.0, child.boundingRect().width(), 0.0)) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeWin(self, event): + + + cmds.deleteUI("pyART_movePickerToTabWIN", wnd = True) diff --git a/Core/Scripts/Interfaces/ART_PinModules.py b/Core/Scripts/Interfaces/ART_PinModules.py new file mode 100644 index 0000000..4535f64 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_PinModules.py @@ -0,0 +1,196 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import os +import System.utils as utils + + + +class ART_PinModules(): + def __init__(self, mainUI): + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + #build the UI + self.buildUI() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + + if cmds.window("ART_PinModulesWin", exists = True): + cmds.deleteUI("ART_PinModulesWin", wnd = True) + + #launch a UI to get the name information + self.window = QtWidgets.QMainWindow(self.mainUI) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.window.setStyleSheet(self.style) + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.window.setCentralWidget(self.mainWidget) + + #set qt object name + self.window.setObjectName("ART_PinModulesWin") + self.window.setWindowTitle("Pin Modules") + + #create the mainLayout for the rig creator UI + self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) + self.mainLayout.setContentsMargins(0, 0, 0, 0) + + self.window.resize(400, 250) + self.window.setSizePolicy(mainSizePolicy) + self.window.setMinimumSize(QtCore.QSize( 400, 250 )) + self.window.setMaximumSize(QtCore.QSize( 400, 250 )) + + #create the background image + self.frame = QtWidgets.QFrame() + self.mainLayout.addWidget(self.frame) + + #create the layout for the widgets + self.widgetLayout = QtWidgets.QHBoxLayout(self.frame) + self.widgetLayout.setContentsMargins(5, 5, 5, 5) + + + #left side == list of modules in scene. for each item in list, will do something similar to aim mode, where we will toggle an icon for pin state + self.moduleList = QtWidgets.QListWidget() + self.widgetLayout.addWidget(self.moduleList) + self.moduleList.setMinimumSize(QtCore.QSize( 265, 200 )) + self.moduleList.setMaximumSize(QtCore.QSize( 265, 200 )) + self.moduleList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.moduleList.setSpacing(3) + + #right side layout == select all, clear selection, Pin Selected buttons + self.buttonLayout = QtWidgets.QVBoxLayout() + self.widgetLayout.addLayout(self.buttonLayout) + self.buttonLayout.setContentsMargins(5, 20, 5, 20) + + #add the selection buttons + self.selectAllButton = QtWidgets.QPushButton("Select All") + self.selectAllButton.setMinimumSize(QtCore.QSize( 115, 25 )) + self.selectAllButton.setMaximumSize(QtCore.QSize( 115, 25 )) + self.buttonLayout.addWidget(self.selectAllButton) + self.selectAllButton.clicked.connect(self.moduleList.selectAll) + self.selectAllButton.setObjectName("blueButton") + + self.selectNoneButton = QtWidgets.QPushButton("Clear Selection") + self.selectNoneButton.setMinimumSize(QtCore.QSize( 115, 25 )) + self.selectNoneButton.setMaximumSize(QtCore.QSize( 115, 25 )) + self.buttonLayout.addWidget(self.selectNoneButton) + self.selectNoneButton.clicked.connect(self.moduleList.clearSelection) + self.selectNoneButton.setObjectName("blueButton") + + #spacer + spacerItem = QtWidgets.QSpacerItem(20, 80, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.buttonLayout.addItem(spacerItem) + + #add the buttons for reset settings and reset transforms + self.pinBtn = QtWidgets.QPushButton("Pin Selected") + self.pinBtn.setMinimumSize(QtCore.QSize( 115, 25 )) + self.pinBtn.setMaximumSize(QtCore.QSize( 115, 25 )) + self.buttonLayout.addWidget(self.pinBtn) + self.pinBtn.setToolTip("Pin the selected modules so that parent module movements do not effect the pinned module") + self.pinBtn.clicked.connect(partial(self.toggleLock, True)) + self.pinBtn.setObjectName("blueButton") + + self.unpinBtn = QtWidgets.QPushButton("Unpin Selected") + self.unpinBtn.setMinimumSize(QtCore.QSize( 115, 25 )) + self.unpinBtn.setMaximumSize(QtCore.QSize( 115, 25 )) + self.buttonLayout.addWidget(self.unpinBtn) + self.unpinBtn.setToolTip("Unpin modules to resume normal module behavior") + self.unpinBtn.clicked.connect(partial(self.toggleLock, False)) + self.unpinBtn.setObjectName("blueButton") + + + + #populate the list widget + modules = utils.returnRigModules() + for module in modules: + #get module name + moduleName = cmds.getAttr(module + ".moduleName") + + #font + headerFont = QtGui.QFont() + headerFont.setPointSize(10) + headerFont.setBold(True) + + #create the listWidgetItem + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/locked.png")) + iconOff = QtGui.QIcon(os.path.join(self.iconsPath, "System/unlocked.png")) + + item = QtWidgets.QListWidgetItem(iconOff, " " + moduleName) + item.setFont(headerFont) + item.setData(QtCore.Qt.UserRole, [icon, iconOff]) + + pinState = cmds.getAttr(module + ".pinned") + if pinState: + item.setIcon(icon) + + + self.moduleList.addItem(item) + + + #show the window + self.window.show() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleLock(self, state): + + selected = self.moduleList.selectedItems() + items = [] + + for each in selected: + items.append(each.text()) + + + if state: + each.setIcon(each.data(QtCore.Qt.UserRole)[0]) + + for inst in self.mainUI.moduleInstances: + name = inst.name + if each.text().strip() == name: + networkNode = inst.returnNetworkNode + cmds.setAttr(networkNode + ".pinned", lock = False) + cmds.setAttr(networkNode + ".pinned", True, lock = True) + inst.pinModule(True) + + + + if not state: + each.setIcon(each.data(QtCore.Qt.UserRole)[1]) + + for inst in self.mainUI.moduleInstances: + name = inst.name + if each.text().strip() == name: + networkNode = inst.returnNetworkNode + cmds.setAttr(networkNode + ".pinned", lock = False) + cmds.setAttr(networkNode + ".pinned", False, lock = True) + inst.pinModule(False) + + + #clear selection + self.moduleList.clearSelection()
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/ART_Publish.py b/Core/Scripts/Interfaces/ART_Publish.py new file mode 100644 index 0000000..c97a069 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_Publish.py @@ -0,0 +1,2394 @@ +from functools import partial +import maya.cmds as cmds +import os, json +import maya.OpenMayaUI as mui +import maya.OpenMaya as openMaya +import System.utils as utils + +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +#maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + + + +class ART_Publish(): + #Original Author: Jeremy Ernst + + def __init__(self, mainUI): + + #super(ART_Publish, self).__init__(parent = None) + + #publish file info + self.publishFileInfo = [] + self.currentModule = None + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.projectPath = settings.value("projectPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + + #images + self.imageBkgrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/toolbar_background.png")) + self.imageBtnBkrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/blue_field_background.png")) + self.frameBackground = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/field_background.png")) + + + #build the UI + if cmds.window("ART_PublishWin", exists = True): + cmds.deleteUI("ART_PublishWin", wnd = True) + + #create model poses + for inst in self.mainUI.moduleInstances: + if inst.name != "root": + #call on the module's bakeOffsets method + inst.aimMode_Setup(False) + inst.getReferencePose("modelPose") + + self.buildPublishUI() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildPublishUI(self): + #Original Author: Jeremy Ernst + + #create the main window + self.mainWin = QtWidgets.QMainWindow(self.mainUI) + self.mainWin.closeEvent = self.closeWin + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.mainWin.setStyleSheet(self.style) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWidget.setStyleSheet(self.style) + self.mainWin.setCentralWidget(self.mainWidget) + + #set qt object name + self.mainWin.setObjectName("ART_PublishWin") + self.mainWin.setWindowTitle("Publish") + + #font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + #set size policy + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + #create the menu bar + self.menuBar = QtWidgets.QMenuBar() + self.menuBar.setMaximumHeight(20) + self.layout.addWidget(self.menuBar) + + #add items to menu bar + helpMenu = self.menuBar.addMenu("Help") + helpMenu.addAction("Help On Publish..", self.publishHelp) + helpMenu.addAction("Help On Create Rig Pose..", self.rigPoseHelp) + + self.mainWin.resize(600, 400) + self.mainWin.setSizePolicy(mainSizePolicy) + self.mainWin.setMinimumSize(QtCore.QSize( 600, 400 )) + self.mainWin.setMaximumSize(QtCore.QSize( 600, 400 )) + + #Create a stackedWidget + self.stackWidget = QtWidgets.QStackedWidget() + self.layout.addWidget(self.stackWidget) + + #build pages + self.createInfoPage() + self.createProjectPage() + self.createRigPosePage() + self.createMeshSlicerPage() + self.createThumbnailCreatorPage() + self.createSummaryPage() + + + #show window + self.mainWin.show() + self.stackWidget.setCurrentIndex(0) + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createInfoPage(self): + #Original Author: Jeremy Ernst + + #create the QFrame for this page + self.infoPage = QtWidgets.QFrame() + self.stackWidget.addWidget(self.infoPage) + self.infoPageMainLayout = QtWidgets.QVBoxLayout(self.infoPage) + self.infoPage.setObjectName("epic") + self.infoPage.setStyleSheet(self.style) + + #label + infoLabel = QtWidgets.QLabel("Publish Your Rig") + infoLabel.setStyleSheet("background: transparent;") + self.infoPageMainLayout.addWidget(infoLabel) + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + infoLabel.setFont(font) + + #image + self.infoFrame = QtWidgets.QFrame() + self.infoFrame.setMinimumSize(QtCore.QSize( 560, 250 )) + self.infoFrame.setMaximumSize(QtCore.QSize( 560, 250 )) + self.infoPageMainLayout.addWidget(self.infoFrame) + + + #add the image showing the information to the user + image = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/publishInfo.png")) + self.infoFrame.setStyleSheet("background-image: url(" + image + ");") + + #buttons + self.infoPageButtonlayout = QtWidgets.QHBoxLayout() + self.infoPageMainLayout.addLayout(self.infoPageButtonlayout) + self.cancelPublishBtn = QtWidgets.QPushButton("Cancel") + self.cancelPublishBtn.setMinimumHeight(50) + self.cancelPublishBtn.setFont(font) + self.continuePublishBtn = QtWidgets.QPushButton("Continue") + self.continuePublishBtn.setMinimumHeight(50) + self.continuePublishBtn.setFont(font) + self.infoPageButtonlayout.addWidget(self.cancelPublishBtn) + self.infoPageButtonlayout.addWidget(self.continuePublishBtn) + + + #button styling + self.cancelPublishBtn.setObjectName("blueButton") + self.continuePublishBtn.setObjectName("blueButton") + + #button hookups + self.cancelPublishBtn.clicked.connect(partial(self.cancelPublish)) + self.continuePublishBtn.clicked.connect(partial(self.continueToProject)) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createProjectPage(self): + #Original Author: Jeremy Ernst + + #create the QFrame for this page + self.projPage = QtWidgets.QFrame() + self.stackWidget.addWidget(self.projPage) + self.projPageMainLayout = QtWidgets.QHBoxLayout(self.projPage) + self.projPage.setStyleSheet(self.style) + self.projPage.setObjectName("dark") + + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + + + #the HBoxLayout will have 2 VBoxLayouts + self.projectPageLeftColumn = QtWidgets.QVBoxLayout() + self.projectPageRightColumn = QtWidgets.QVBoxLayout() + self.projPageMainLayout.addLayout(self.projectPageLeftColumn) + self.projPageMainLayout.addLayout(self.projectPageRightColumn) + + # #Left Column # # + #2 HBox children that contain comboBox + pushButton, listWidget that lists characters in that project/group + + #project + self.projectCbLayout = QtWidgets.QHBoxLayout() + self.projectPageLeftColumn.addLayout(self.projectCbLayout) + + self.projectComboBox = QtWidgets.QComboBox() + self.projectCbLayout.addWidget(self.projectComboBox) + self.projectComboBox.setMaximumWidth(200) + self.projectComboBox.setMaximumHeight(30) + self.projectComboBox.currentIndexChanged.connect(self.populateGroups) + self.projectComboBox.setToolTip("Choose a project to build the rig to.") + + self.addProjectBtn = QtWidgets.QPushButton("+") + self.addProjectBtn.setFont(font) + self.addProjectBtn.setMaximumWidth(30) + self.addProjectBtn.setMaximumHeight(30) + self.addProjectBtn.setToolTip("Add a new project to the list.") + self.projectCbLayout.addWidget(self.addProjectBtn) + self.addProjectBtn.clicked.connect(partial(self.addNewProject, True)) + self.addProjectBtn.setToolTip("Create a new project.") + self.addProjectBtn.setObjectName("blueButton") + + #group + self.groupCbLayout = QtWidgets.QHBoxLayout() + self.projectPageLeftColumn.addLayout(self.groupCbLayout) + + self.groupComboBox = QtWidgets.QComboBox() + self.groupCbLayout.addWidget(self.groupComboBox) + self.groupComboBox.setMaximumWidth(200) + self.groupComboBox.setMaximumHeight(30) + self.groupComboBox.currentIndexChanged.connect(self.populateCharacters) + self.groupComboBox.setToolTip("Choose a group to build the rig to in the selected project. (optional)") + + self.addGroupBtn = QtWidgets.QPushButton("+") + self.addGroupBtn.setFont(font) + self.addGroupBtn.setMaximumWidth(30) + self.addGroupBtn.setMaximumHeight(30) + self.addGroupBtn.setToolTip("Add a new group to the list.") + self.groupCbLayout.addWidget(self.addGroupBtn) + self.addGroupBtn.clicked.connect(self.addNewGroup) + self.addGroupBtn.setToolTip("Create a new group.") + self.addGroupBtn.setObjectName("blueButton") + + + + #listWidget + self.characterList = QtWidgets.QListWidget() + self.characterList.setMaximumWidth(230) + self.projectPageLeftColumn.addWidget(self.characterList) + self.characterList.itemClicked.connect(self.setCharacterName) + self.characterList.setToolTip("List of characters in the specified character and group.") + self.characterList.setFont(font) + self.characterList.setSpacing(3) + + # #Right Column # # + #line edit for character name, 2 hboxLayouts for pre/post script path lineEdits + pushButtons, spacers, and continue button + + #character name + style = """ + QLineEdit + { + border: 2px solid rgb(0,0,0); + border-radius: 5px; + background-color: rgb(80,80,80); + font: bold 30px; + selection-background-color: black; + } + + QLineEdit::focus + { + border: 1px solid rgb(25,175,255); + + } + """ + self.characterName = QtWidgets.QLineEdit() + self.projectPageRightColumn.addWidget(self.characterName) + self.characterName.setMinimumHeight(75) + self.characterName.setMaximumHeight(75) + self.characterName.setPlaceholderText("Asset Name") + self.characterName.setStyleSheet(style) + self.characterName.setAlignment(QtCore.Qt.AlignCenter) + spacerItem = QtWidgets.QSpacerItem(20, 200, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.projectPageRightColumn.addItem(spacerItem) + self.characterName.setToolTip("Name the asset will be published with.") + + #character node + characterNode = utils.returnCharacterModule() + attrs = cmds.listAttr(characterNode, ud = True) + + #select character, if applicable + version = 0 + for attr in attrs: + if attr.find("version") == 0: + version = cmds.getAttr(characterNode + ".version") + + if version > 0: + #change type (for versioning) + self.changeTypeLayout = QtWidgets.QHBoxLayout() + self.projectPageRightColumn.addLayout(self.changeTypeLayout) + + label = QtWidgets.QLabel("Change Type:") + label.setStyleSheet("background: transparent;") + self.changeTypeLayout.addWidget(label) + self.changeType = QtWidgets.QComboBox() + self.changeTypeLayout.addWidget(self.changeType) + + self.changeType.addItem("Cosmetic Change") + self.changeType.addItem("Minor Change") + self.changeType.addItem("Major Change") + self.changeType.setToolTip("The type of change that was done, leading to a republish.") + + #add note box for revision + noteLabel = QtWidgets.QLabel("Revision Note (optional):") + noteLabel.setStyleSheet("background: transparent;") + self.projectPageRightColumn.addWidget(noteLabel) + + self.revisionNote = QtWidgets.QTextEdit() + self.projectPageRightColumn.addWidget(self.revisionNote) + self.revisionNote.setObjectName("light") + + spacerItem = QtWidgets.QSpacerItem(20, 200, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.projectPageRightColumn.addItem(spacerItem) + + + #pre script + self.preScriptLayout = QtWidgets.QHBoxLayout() + self.projectPageRightColumn.addLayout(self.preScriptLayout) + + self.preScriptLineEdit = QtWidgets.QLineEdit() + self.preScriptLineEdit.setMinimumHeight(30) + self.preScriptLineEdit.setMaximumHeight(30) + self.preScriptLineEdit.setPlaceholderText("Pre-Script (optional)") + self.preScriptLayout.addWidget(self.preScriptLineEdit) + self.preScriptLineEdit.setToolTip("If you want to run any custom code before the rig is built, load in a MEL or Python script here.") + + self.preScriptBrowse = QtWidgets.QPushButton() + self.preScriptLayout.addWidget(self.preScriptBrowse) + self.preScriptBrowse.setMinimumSize(35,35) + self.preScriptBrowse.setMaximumSize(35, 35) + self.preScriptBrowse.clicked.connect(partial(self.addCustomScripts, True, False)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/fileBrowse.png")) + self.preScriptBrowse.setIconSize(QtCore.QSize(30,30)) + self.preScriptBrowse.setIcon(icon) + + #post script + self.postScriptLayout = QtWidgets.QHBoxLayout() + self.projectPageRightColumn.addLayout(self.postScriptLayout) + + self.postScriptLineEdit = QtWidgets.QLineEdit() + self.postScriptLineEdit.setMinimumHeight(30) + self.postScriptLineEdit.setMaximumHeight(30) + self.postScriptLineEdit.setPlaceholderText("Post-Script (optional)") + self.postScriptLayout.addWidget(self.postScriptLineEdit) + self.postScriptLineEdit.setToolTip("If you want to run any custom code after the rig is built, load in a MEL or Python script here.") + + self.postScriptBrowse = QtWidgets.QPushButton() + self.postScriptLayout.addWidget(self.postScriptBrowse) + self.postScriptBrowse.setMinimumSize(35,35) + self.postScriptBrowse.setMaximumSize(35, 35) + self.postScriptBrowse.clicked.connect(partial(self.addCustomScripts, False, True)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/fileBrowse.png")) + self.postScriptBrowse.setIconSize(QtCore.QSize(30,30)) + self.postScriptBrowse.setIcon(icon) + + + spacerItem = QtWidgets.QSpacerItem(20, 50, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.projectPageRightColumn.addItem(spacerItem) + + + #continue btn + self.goToRigPoseBtn = QtWidgets.QPushButton("Continue") + self.goToRigPoseBtn.setFont(font) + self.goToRigPoseBtn.setMinimumHeight(50) + self.goToRigPoseBtn.setMaximumHeight(50) + self.projectPageRightColumn.addWidget(self.goToRigPoseBtn) + self.goToRigPoseBtn.clicked.connect(self.createNewCharacter) + self.goToRigPoseBtn.setObjectName("blueButton") + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createRigPosePage(self): + #Original Author: Jeremy Ernst + + #create the QFrame for this page + self.rigPosePage = QtWidgets.QFrame() + self.rigPosePage.setMinimumSize(560, 250) + self.stackWidget.addWidget(self.rigPosePage) + self.rpPageMainLayout = QtWidgets.QHBoxLayout(self.rigPosePage) + self.rigPosePage.setStyleSheet(self.style) + self.rigPosePage.setObjectName("dark") + + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + #the HBoxLayout will have 2 VBoxLayouts + self.rpPageLeftColumn = QtWidgets.QVBoxLayout() + self.rpPageRightColumn = QtWidgets.QVBoxLayout() + self.rpPageMainLayout.addLayout(self.rpPageLeftColumn) + self.rpPageMainLayout.addLayout(self.rpPageRightColumn) + + + #left column (list of modules, back button) + self.rpPage_moduleList = QtWidgets.QListWidget() + self.rpPage_moduleList.setMinimumSize(200, 280) + self.rpPage_moduleList.setMaximumSize(200, 280) + self.rpPageLeftColumn.addWidget(self.rpPage_moduleList) + self.rpPage_moduleList.setToolTip("This list of modules make up your character.\nSelect a module from the list to alter the rig \npose for that module.\n\nA rig pose is the ideal pose for each\nmodule for rigging. For legs, this means that\nthe leg is coplanar for the IK solve and\nthe feet are aligned to the world.\n\nFor arms, the same. It's the T-Pose that's\nbest suited to building the rig, since models\nare not always built in that pose.") + self.rpPage_moduleList.setFont(font) + self.rpPage_moduleList.setSpacing(3) + + #populate list + modules = utils.returnRigModules() + for mod in modules: + name = cmds.getAttr(mod + ".moduleName") + if name != "root": + self.rpPage_moduleList.addItem(name) + + self.rpPage_moduleList.itemClicked.connect(self.moduleSelected) + + #button layout + self.rpPage_leftButtonLayout = QtWidgets.QHBoxLayout() + self.rpPageLeftColumn.addLayout(self.rpPage_leftButtonLayout) + self.rpPage_leftButtonLayout.setContentsMargins(0,0,100,0) + + #button/spacer + self.rpPage_backButton = QtWidgets.QPushButton("Back") + self.rpPage_leftButtonLayout.addWidget(self.rpPage_backButton) + self.rpPage_backButton.setFont(font) + self.rpPage_backButton.setMinimumHeight(50) + self.rpPage_backButton.setMaximumHeight(50) + self.rpPage_backButton.clicked.connect(self.backToProject) + self.rpPage_backButton.setObjectName("blueButton") + + + #right column (scrollArea for module settings) + + #scroll area contents + self.rpPage_scrollContents = QtWidgets.QFrame() + scrollSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) + self.rpPage_scrollContents.setSizePolicy(scrollSizePolicy) + self.rpPage_scrollContents.setObjectName("dark") + + + #scroll area + self.rpPage_Settings = QtWidgets.QScrollArea() + self.rpPage_Settings.setMinimumSize(350, 280) + self.rpPage_Settings.setMaximumSize(350, 280) + self.rpPageRightColumn.addWidget(self.rpPage_Settings) + self.rpPage_Settings.setWidgetResizable(True) + self.rpPage_Settings.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.rpPage_Settings.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.rpPage_Settings.setWidget(self.rpPage_scrollContents) + + + #layout for scroll area + self.rpPage_layout = QtWidgets.QVBoxLayout(self.rpPage_scrollContents) + + #stacked widget + self.rpPage_stackWidget = QtWidgets.QStackedWidget() + self.rpPage_layout.addWidget(self.rpPage_stackWidget) + + + #message + self.message = QtWidgets.QLabel("Select a module from the list to adjust the rig pose for that module.") + self.message.setStyleSheet("background: transparent;") + self.message.setAlignment(QtCore.Qt.AlignCenter) + self.rpPage_stackWidget.addWidget(self.message) + + #button layout + self.rpPage_righttButtonLayout = QtWidgets.QHBoxLayout() + self.rpPageRightColumn.addLayout(self.rpPage_righttButtonLayout) + self.rpPage_righttButtonLayout.setContentsMargins(100,0,0,0) + + #button/spacer + self.rpPage_continueButton = QtWidgets.QPushButton("Continue") + self.rpPage_righttButtonLayout.addWidget(self.rpPage_continueButton) + self.rpPage_continueButton.setFont(font) + self.rpPage_continueButton.setMinimumHeight(50) + self.rpPage_continueButton.setMaximumHeight(50) + self.rpPage_continueButton.clicked.connect(partial(self.continueToCreateAnimMesh)) + self.rpPage_continueButton.setObjectName("blueButton") + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createMeshSlicerPage(self): + #Original Author: Jeremy Ernst + + #create the QFrame for this page + self.meshSlicerPage = QtWidgets.QFrame() + self.meshSlicerPage.setMinimumSize(560, 250) + self.stackWidget.addWidget(self.meshSlicerPage) + self.msPageMainLayout = QtWidgets.QVBoxLayout(self.meshSlicerPage) + + #info page styling + self.meshSlicerPage.setStyleSheet(self.style) + self.meshSlicerPage.setObjectName("epic") + + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + #create the label for the instructional gif + self.movie_screen = QtWidgets.QLabel() + + # expand and center the label + self.movie_screen.setAlignment(QtCore.Qt.AlignCenter) + self.msPageMainLayout.addWidget(self.movie_screen) + + #set movie from file path + gif = utils.returnFriendlyPath(os.path.join(self.iconsPath, "Help/meshSlicer.gif")) + self.movie = QtGui.QMovie(gif, QtCore.QByteArray()) + self.movie.setCacheMode(QtGui.QMovie.CacheAll) + self.movie.setSpeed(100) + self.movie_screen.setMovie(self.movie) + + #button layout + self.msPageButtonLayout = QtWidgets.QHBoxLayout() + self.msPageMainLayout.addLayout(self.msPageButtonLayout) + + self.msPageBackBtn = QtWidgets.QPushButton("Back") + self.msPageBackBtn.setMinimumHeight(50) + self.msPageBackBtn.setFont(font) + self.msPageBackBtn.clicked.connect(partial(self.backToRigPose)) + self.msPageBackBtn.setObjectName("blueButton") + + self.msPageSkipBtn = QtWidgets.QPushButton("Skip") + self.msPageSkipBtn.setMinimumHeight(50) + self.msPageSkipBtn.setFont(font) + self.msPageSkipBtn.clicked.connect(partial(self.skipToCreateThumbnail)) + self.msPageSkipBtn.setObjectName("blueButton") + + self.mePageContBtn = QtWidgets.QPushButton("Continue") + self.mePageContBtn.setMinimumHeight(50) + self.mePageContBtn.setFont(font) + self.mePageContBtn.clicked.connect(partial(self.continueToCreateThumbnail)) + self.mePageContBtn.setObjectName("blueButton") + + self.msPageButtonLayout.addWidget(self.msPageBackBtn) + spacer = QtWidgets.QSpacerItem(200, 20) + self.msPageButtonLayout.addSpacerItem(spacer) + self.msPageButtonLayout.addWidget(self.msPageSkipBtn) + self.msPageButtonLayout.addWidget(self.mePageContBtn) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createThumbnailCreatorPage(self): + #Original Author: Jeremy Ernst + + #create the QFrame for this page + self.thumbCreatorPage = QtWidgets.QFrame() + self.thumbCreatorPage.setMinimumSize(560, 250) + self.stackWidget.addWidget(self.thumbCreatorPage) + self.tcPageMainLayout = QtWidgets.QVBoxLayout(self.thumbCreatorPage) + + #info page styling + self.thumbCreatorPage.setStyleSheet(self.style) + self.thumbCreatorPage.setObjectName("dark") + + font = QtGui.QFont() + font.setPointSize(20) + font.setBold(True) + + buttonFont = QtGui.QFont() + buttonFont.setPointSize(12) + buttonFont.setBold(True) + + + #viewport and tabs layout + self.tcPageViewportLayout = QtWidgets.QHBoxLayout() + self.tcPageMainLayout.addLayout(self.tcPageViewportLayout) + self.viewportToggle = QtWidgets.QStackedWidget() + self.viewportToggle.setMinimumSize(200,200) + self.viewportToggle.setMaximumSize(200,200) + self.tcPageViewportLayout.addWidget(self.viewportToggle) + + #custom image loaded + self.customImg = QtWidgets.QFrame() + self.customImg.setMinimumSize(200,200) + self.customImg.setMaximumSize(200,200) + self.viewportToggle.addWidget(self.customImg) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #first element in this layout is our viewport + + #create the camera for the viewport + self.thumbnailCamera = cmds.camera(name = "thumbnail_camera")[0] + constraint = cmds.parentConstraint("persp", self.thumbnailCamera) + cmds.setAttr(self.thumbnailCamera + ".v", 0) + cmds.lockNode(self.thumbnailCamera, lock = True) + + #create light rig + self.lightGrp = cmds.group(empty = True, name = "thumbnail_lights") + + #key light + self.spot1 = cmds.spotLight(rgb = (.902, .785, .478), name = "thumbnail_spot1", ca = 100, do = 1) + cmds.setAttr(self.spot1 + ".useDepthMapShadows", 1) + self.spot1Parent = cmds.listRelatives(self.spot1, parent = True)[0] + cmds.setAttr(self.spot1Parent + ".tx", 150) + cmds.setAttr(self.spot1Parent + ".ty", -150) + cmds.setAttr(self.spot1Parent + ".tz", 300) + aim = cmds.aimConstraint("root", self.spot1Parent, aimVector = [0,0,-1], upVector = [0,1,0], worldUpType = "scene")[0] + cmds.delete(aim) + + #bounce light + self.spot2 = cmds.spotLight(rgb = (.629, .799, .949), name = "thumbnail_spot2", ca = 100, do = 1) + cmds.setAttr(self.spot2 + ".useDepthMapShadows", 1) + self.spot2Parent = cmds.listRelatives(self.spot2, parent = True)[0] + cmds.setAttr(self.spot2Parent + ".tx", -150) + cmds.setAttr(self.spot2Parent + ".ty", -150) + cmds.setAttr(self.spot2Parent + ".tz", 300) + aim = cmds.aimConstraint("root", self.spot2Parent, aimVector = [0,0,-1], upVector = [0,1,0], worldUpType = "scene")[0] + cmds.delete(aim) + + #fill light + self.spot3 = cmds.spotLight(rgb = (1, 1, 1), name = "thumbnail_spot3", ca = 100, do = 1) + cmds.setAttr(self.spot3 + ".useDepthMapShadows", 1) + self.spot3Parent = cmds.listRelatives(self.spot3, parent = True)[0] + cmds.setAttr(self.spot3Parent + ".tx", 0) + cmds.setAttr(self.spot3Parent + ".ty", 150) + cmds.setAttr(self.spot3Parent + ".tz", 300) + aim = cmds.aimConstraint("root", self.spot3Parent, aimVector = [0,0,-1], upVector = [0,1,0], worldUpType = "scene")[0] + cmds.delete(aim) + + cmds.parent([self.spot1Parent, self.spot2Parent, self.spot3Parent], self.lightGrp) + + cmds.lockNode(self.spot1Parent, lock = True) + cmds.lockNode(self.spot2Parent, lock = True) + cmds.lockNode(self.spot3Parent, lock = True) + cmds.lockNode(self.lightGrp, lock = True) + + + #model editor + self.tcViewport = cmds.modelEditor(camera = self.thumbnailCamera, dl = "all", da = "smoothShaded", hud = False, gr = False, dtx = True, sdw = True, j = False, ca = False, lt = False) + pointer = mui.MQtUtil.findControl(self.tcViewport) + self.tcViewWidget = shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + self.viewportToggle.addWidget(self.tcViewWidget) + self.tcViewWidget.setMinimumSize(200,200) + self.tcViewWidget.setMaximumSize(200,200) + self.viewportToggle.setCurrentIndex(1) + + #add image plane with image + imagePlanePath = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/imagePlane.png")) + self.imagePlane = cmds.imagePlane(fn = imagePlanePath, c = self.thumbnailCamera, lt = self.thumbnailCamera, sia = False ) + cmds.setAttr(self.imagePlane[1] + ".depth", 3000) + cmds.setAttr(self.imagePlane[1] + ".sizeX", 2) + cmds.setAttr(self.imagePlane[1] + ".sizeY", 2) + + #second element in this layout is a tabWidget + + #tab stylesheet (tab stylesheet via QSS doesn't seem to work for some reason + stylesheet = """ + QTabBar::tab + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(90,90,90), stop:1 rgb(30,30,30)); + border: 2px solid black; + width: 180px; + padding-left: -10px; + font: 8pt; + } + QTabBar::tab:selected + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(255,174,0), stop:1 rgb(30,30,30)); + border: 2px solid black; + font: bold 10pt; + } + QTabBar::tab:hover + { + background: rgb(132,95,16); + font: bold 10pt; + } + QTabBar::tab:!selected + { + margin-top: 5px; + } + QTabWidget::pane + { + border: 2px solid rgb(0,0,0); + } + """ + + + self.tcPageTabs = QtWidgets.QTabWidget() + self.tcPageTabs.setStyleSheet(stylesheet) + self.tcPageViewportLayout.addWidget(self.tcPageTabs) + self.tcPageTabs.setMinimumHeight(200) + self.tcPageTabs.setMaximumHeight(200) + + + + # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # + #rendering tab + self.renderTab = QtWidgets.QFrame() + self.tcPageTabs.addTab(self.renderTab, "LIGHT OPTIONS") + self.renderTab.setObjectName("epic") + + #rendering tab main layout + self.renderTabLayout = QtWidgets.QHBoxLayout(self.renderTab) + self.renderTabLayout.setSpacing(10) + + #left column of render tab options + self.renderTabLeft = QtWidgets.QVBoxLayout() + self.renderTabLayout.addLayout(self.renderTabLeft) + + icon = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/spotLight.png")) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #light 1 + self.light1Layout = QtWidgets.QHBoxLayout() + self.renderTabLeft.addLayout(self.light1Layout) + + self.light1Img = QtWidgets.QPushButton("") + self.light1Img.setFont(font) + self.light1Img.setMinimumSize(40,40) + self.light1Img.setMaximumSize(40,40) + self.light1Img.setStyleSheet("background-image: url(" + icon + "); border: black solid 0px;") + self.light1Layout.addWidget(self.light1Img) + + self.light1Dial = QtWidgets.QDial() + self.light1Dial.setToolTip("Light Intensity") + self.light1Layout.addWidget(self.light1Dial) + self.light1Dial.setMinimumSize(50,50) + self.light1Dial.setMaximumSize(50,50) + self.light1Dial.setRange(0,100) + self.light1Dial.setValue(50) + shadow1 = QtWidgets.QGraphicsDropShadowEffect(self.light1Dial) + shadow1.setBlurRadius(5) + shadow1.setColor(QtGui.QColor (0, 0, 0, 255)) + self.light1Dial.setStyleSheet("background-color: rgb(60, 60, 60);") + self.light1Dial.setGraphicsEffect(shadow1) + self.light1Dial.valueChanged.connect(partial(self.changeLightIntensity, self.spot1, self.light1Dial)) + + self.light1Swatch = QtWidgets.QPushButton("Color") + self.light1Layout.addWidget(self.light1Swatch) + self.light1Swatch.setMinimumSize(60,30) + self.light1Swatch.setMaximumSize(60,30) + self.light1Swatch.setFont(buttonFont) + self.light1Swatch.setStyleSheet("color: rgb(230, 200, 122);") + shadow2 = QtWidgets.QGraphicsDropShadowEffect(self.light1Swatch) + shadow2.setBlurRadius(5) + shadow2.setColor(QtGui.QColor (0, 0, 0, 255)) + self.light1Swatch.setGraphicsEffect(shadow2) + self.light1Swatch.clicked.connect(partial(self.changeLightColor, self.spot1, self.light1Swatch)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #light 2 + self.light2Layout = QtWidgets.QHBoxLayout() + self.renderTabLeft.addLayout(self.light2Layout) + + self.light2Img = QtWidgets.QPushButton("") + self.light2Img.setFont(font) + self.light2Img.setMinimumSize(40,40) + self.light2Img.setMaximumSize(40,40) + self.light2Img.setStyleSheet("background-image: url(" + icon + "); border: black solid 0px;") + self.light2Layout.addWidget(self.light2Img) + + self.light2Dial = QtWidgets.QDial() + self.light2Dial.setToolTip("Light Intensity") + self.light2Layout.addWidget(self.light2Dial) + self.light2Dial.setMinimumSize(50,50) + self.light2Dial.setMaximumSize(50,50) + self.light2Dial.setRange(0,100) + self.light2Dial.setValue(50) + shadow1 = QtWidgets.QGraphicsDropShadowEffect(self.light2Dial) + shadow1.setBlurRadius(5) + shadow1.setColor(QtGui.QColor (0, 0, 0, 255)) + self.light2Dial.setStyleSheet("background-color: rgb(60, 60, 60);") + self.light2Dial.setGraphicsEffect(shadow1) + self.light2Dial.valueChanged.connect(partial(self.changeLightIntensity, self.spot2, self.light2Dial)) + + self.light2Swatch = QtWidgets.QPushButton("Color") + self.light2Layout.addWidget(self.light2Swatch) + self.light2Swatch.setMinimumSize(60,30) + self.light2Swatch.setMaximumSize(60,30) + self.light2Swatch.setFont(buttonFont) + self.light2Swatch.setStyleSheet("color: rgb(160, 204, 242);") + shadow2 = QtWidgets.QGraphicsDropShadowEffect(self.light2Swatch) + shadow2.setBlurRadius(5) + shadow2.setColor(QtGui.QColor (0, 0, 0, 255)) + self.light2Swatch.setGraphicsEffect(shadow2) + self.light2Swatch.clicked.connect(partial(self.changeLightColor, self.spot2, self.light2Swatch)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #light 3 + self.light3Layout = QtWidgets.QHBoxLayout() + self.renderTabLeft.addLayout(self.light3Layout) + + self.light3Img = QtWidgets.QPushButton("") + self.light3Img.setFont(font) + self.light3Img.setMinimumSize(40,40) + self.light3Img.setMaximumSize(40,40) + self.light3Img.setStyleSheet("background-image: url(" + icon + "); border: black solid 0px;") + self.light3Layout.addWidget(self.light3Img) + + self.light3Dial = QtWidgets.QDial() + self.light3Dial.setToolTip("Light Intensity") + self.light3Layout.addWidget(self.light3Dial) + self.light3Dial.setMinimumSize(50,50) + self.light3Dial.setMaximumSize(50,50) + self.light3Dial.setRange(0,100) + self.light3Dial.setValue(50) + shadow1 = QtWidgets.QGraphicsDropShadowEffect(self.light3Dial) + shadow1.setBlurRadius(5) + shadow1.setColor(QtGui.QColor (0, 0, 0, 255)) + self.light3Dial.setStyleSheet("background-color: rgb(60, 60, 60);") + self.light3Dial.setGraphicsEffect(shadow1) + self.light3Dial.valueChanged.connect(partial(self.changeLightIntensity, self.spot3, self.light3Dial)) + + self.light3Swatch = QtWidgets.QPushButton("Color") + self.light3Layout.addWidget(self.light3Swatch) + self.light3Swatch.setMinimumSize(60,30) + self.light3Swatch.setMaximumSize(60,30) + self.light3Swatch.setFont(buttonFont) + self.light3Swatch.setStyleSheet("color: rgb(255, 255, 255);") + shadow2 = QtWidgets.QGraphicsDropShadowEffect(self.light3Swatch) + shadow2.setBlurRadius(5) + shadow2.setColor(QtGui.QColor (0, 0, 0, 255)) + self.light3Swatch.setGraphicsEffect(shadow2) + self.light3Swatch.clicked.connect(partial(self.changeLightColor, self.spot3, self.light3Swatch)) + + + #right column + self.renderTabRight = QtWidgets.QVBoxLayout() + self.renderTabLayout.addLayout(self.renderTabRight) + + + #orbit layout + self.orbitLayout = QtWidgets.QHBoxLayout() + self.renderTabRight.addLayout(self.orbitLayout) + + + label = QtWidgets.QLabel("Orbit: ") + label.setStyleSheet("background: transparent;") + label.setFont(buttonFont) + label.setMinimumHeight(30) + label.setMaximumHeight(30) + self.orbitLayout.addWidget(label) + + self.orbitDial = QtWidgets.QDial() + self.orbitDial.setToolTip("Light Rig Position") + self.orbitLayout.addWidget(self.orbitDial) + self.orbitDial.setMinimumSize(75,75) + self.orbitDial.setMaximumSize(75,75) + self.orbitDial.setRange(0,360) + self.orbitDial.setValue(0) + shadow = QtWidgets.QGraphicsDropShadowEffect(self.orbitDial) + shadow.setBlurRadius(5) + shadow.setColor(QtGui.QColor (0, 0, 0, 255)) + self.orbitDial.setGraphicsEffect(shadow) + self.orbitDial.valueChanged.connect(partial(self.orbitLights, self.lightGrp, self.orbitDial)) + + + #pitch layout + self.pitchLayout = QtWidgets.QHBoxLayout() + self.renderTabRight.addLayout(self.pitchLayout) + + + label = QtWidgets.QLabel("Pitch: ") + label.setStyleSheet("background: transparent;") + label.setFont(buttonFont) + label.setMinimumHeight(30) + label.setMaximumHeight(30) + self.pitchLayout.addWidget(label) + + + self.pitchSlider = QtWidgets.QSlider() + self.pitchSlider.setOrientation(QtCore.Qt.Horizontal) + self.pitchSlider.setToolTip("Light Aim Height") + self.pitchSlider.setRange(-180, 180) + self.pitchLayout.addWidget(self.pitchSlider) + self.pitchSlider.valueChanged.connect(partial(self.pitchLights, [self.spot1Parent, self.spot2Parent, self.spot3Parent], self.pitchSlider)) + + #options layout + self.tcPageOptionsLayout = QtWidgets.QHBoxLayout() + self.tcPageMainLayout.addLayout(self.tcPageOptionsLayout) + + self.hqRenderCB = QtWidgets.QCheckBox("High Quality") + self.tcPageOptionsLayout.addWidget(self.hqRenderCB) + self.hqRenderCB.clicked.connect(partial(self.highQualityToggle)) + self.hqRenderCB.setChecked(False) + + + self.shadowsCB = QtWidgets.QCheckBox("Shadows") + self.tcPageOptionsLayout.addWidget(self.shadowsCB) + self.shadowsCB.setChecked(False) + self.shadowsCB.clicked.connect(partial(self.shadowsToggle, [self.spot1, self.spot2, self.spot3])) + + self.customThumbnail = QtWidgets.QLineEdit() + self.tcPageOptionsLayout.addWidget(self.customThumbnail) + self.customThumbnail.setStyleSheet("background-image: url(" + self.frameBackground + ");") + + self.loadThumbBtn = QtWidgets.QPushButton("Load Custom") + self.tcPageOptionsLayout.addWidget(self.loadThumbBtn) + self.loadThumbBtn.clicked.connect(self.loadCustomImg) + self.loadThumbBtn.setObjectName("blueButton") + + #button layout + self.tcPageButtonLayout = QtWidgets.QHBoxLayout() + self.tcPageMainLayout.addLayout(self.tcPageButtonLayout) + + self.tcPageBackBtn = QtWidgets.QPushButton("Back") + self.tcPageBackBtn.setMinimumHeight(50) + self.tcPageBackBtn.setFont(font) + self.tcPageBackBtn.clicked.connect(partial(self.backToMeshSlicer)) + self.tcPageBackBtn.setObjectName("blueButton") + + self.tcPageContBtn = QtWidgets.QPushButton("Continue") + self.tcPageContBtn.setMinimumHeight(50) + self.tcPageContBtn.setFont(font) + self.tcPageContBtn.clicked.connect(partial(self.continueToSummary, False)) + self.tcPageContBtn.setObjectName("blueButton") + + self.tcPageButtonLayout.addWidget(self.tcPageBackBtn) + spacer = QtWidgets.QSpacerItem(200, 20) + self.tcPageButtonLayout.addSpacerItem(spacer) + self.tcPageButtonLayout.addWidget(self.tcPageContBtn) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createSummaryPage(self): + #Original Author: Jeremy Ernst + + #create the QFrame for this page + self.summaryPage = QtWidgets.QFrame() + self.summaryPage.setMinimumSize(560, 250) + self.stackWidget.addWidget(self.summaryPage) + self.sumPageMainLayout = QtWidgets.QVBoxLayout(self.summaryPage) + + #info page styling + self.summaryPage.setStyleSheet(self.style) + self.summaryPage.setObjectName("epic") + + font = QtGui.QFont() + font.setPointSize(20) + font.setBold(True) + + buttonFont = QtGui.QFont() + buttonFont.setPointSize(12) + buttonFont.setBold(True) + + #top section of UI will contain Hbox layout with icon and character information + self.sumPageTopLayout = QtWidgets.QHBoxLayout() + self.sumPageMainLayout.addLayout(self.sumPageTopLayout) + + #left side of top layout + self.sumPageIcon = QtWidgets.QFrame() + self.sumPageIcon.setMinimumSize(200,200) + self.sumPageIcon.setMaximumSize(200,200) + self.sumPageTopLayout.addWidget(self.sumPageIcon) + shadow = QtWidgets.QGraphicsDropShadowEffect(self.sumPageIcon) + shadow.setBlurRadius(5) + shadow.setColor(QtGui.QColor (0, 0, 0, 255)) + self.sumPageIcon.setGraphicsEffect(shadow) + + + + #right side of top layout (vbox with 5 hbox children) + self.sumPageTopRight = QtWidgets.QVBoxLayout() + self.sumPageTopLayout.addLayout(self.sumPageTopRight) + + + #character name + assetNameLayout = QtWidgets.QHBoxLayout() + self.sumPageTopRight.addLayout(assetNameLayout) + assetNameLayout.setContentsMargins(3,0,3,0) + + label = QtWidgets.QLabel("Asset Name: ") + label.setStyleSheet("background: transparent;") + assetNameLayout.addWidget(label) + label.setAlignment(QtCore.Qt.AlignLeft) + label.setMinimumWidth(80) + label.setMaximumWidth(80) + + self.sumPageAssetName = QtWidgets.QLineEdit() + assetNameLayout.addWidget(self.sumPageAssetName) + self.sumPageAssetName.setMinimumWidth(240) + self.sumPageAssetName.setMaximumWidth(240) + self.sumPageAssetName.setAlignment(QtCore.Qt.AlignRight) + self.sumPageAssetName.setReadOnly(True) + + #project + projLayout = QtWidgets.QHBoxLayout() + self.sumPageTopRight.addLayout(projLayout) + projLayout.setContentsMargins(3,0,3,0) + + label = QtWidgets.QLabel("Project: ") + label.setStyleSheet("background: transparent;") + projLayout.addWidget(label) + label.setAlignment(QtCore.Qt.AlignLeft) + label.setMinimumWidth(80) + label.setMaximumWidth(80) + + self.sumPageProj = QtWidgets.QLineEdit() + projLayout.addWidget(self.sumPageProj) + self.sumPageProj.setMinimumWidth(240) + self.sumPageProj.setMaximumWidth(240) + self.sumPageProj.setAlignment(QtCore.Qt.AlignRight) + self.sumPageProj.setReadOnly(True) + + + #group + groupNameLayout = QtWidgets.QHBoxLayout() + self.sumPageTopRight.addLayout(groupNameLayout) + groupNameLayout.setContentsMargins(3,0,3,0) + + label = QtWidgets.QLabel("Group: ") + label.setStyleSheet("background: transparent;") + groupNameLayout.addWidget(label) + label.setAlignment(QtCore.Qt.AlignLeft) + label.setMinimumWidth(80) + label.setMaximumWidth(80) + + self.sumPageGroup = QtWidgets.QLineEdit() + groupNameLayout.addWidget(self.sumPageGroup) + self.sumPageGroup.setMinimumWidth(240) + self.sumPageGroup.setMaximumWidth(240) + self.sumPageGroup.setAlignment(QtCore.Qt.AlignRight) + self.sumPageGroup.setReadOnly(True) + + + #revision type + revisionTypeLayout = QtWidgets.QHBoxLayout() + self.sumPageTopRight.addLayout(revisionTypeLayout) + revisionTypeLayout.setContentsMargins(3,0,3,0) + + label = QtWidgets.QLabel("Revision Type: ") + label.setStyleSheet("background: transparent;") + revisionTypeLayout.addWidget(label) + label.setAlignment(QtCore.Qt.AlignLeft) + label.setMinimumWidth(80) + label.setMaximumWidth(80) + + self.sumPageRevisionType = QtWidgets.QLineEdit() + revisionTypeLayout.addWidget(self.sumPageRevisionType) + self.sumPageRevisionType.setMinimumWidth(240) + self.sumPageRevisionType.setMaximumWidth(240) + self.sumPageRevisionType.setAlignment(QtCore.Qt.AlignRight) + self.sumPageRevisionType.setReadOnly(True) + + + #options + optionsLayout = QtWidgets.QHBoxLayout() + self.sumPageTopRight.addLayout(optionsLayout) + optionsLayout.setContentsMargins(3,0,3,0) + + self.sp_preScriptCB = QtWidgets.QCheckBox("Pre-Script?") + optionsLayout.addWidget(self.sp_preScriptCB) + self.sp_preScriptCB.setEnabled(False) + + self.sp_postScriptCB = QtWidgets.QCheckBox("Post-Script?") + optionsLayout.addWidget(self.sp_postScriptCB) + self.sp_postScriptCB.setEnabled(False) + + self.sp_animMeshCB = QtWidgets.QCheckBox("Animation Mesh?") + optionsLayout.addWidget(self.sp_animMeshCB) + self.sp_animMeshCB.setEnabled(False) + + + #button layout + self.sp_PageButtonLayout = QtWidgets.QHBoxLayout() + self.sumPageMainLayout.addLayout(self.sp_PageButtonLayout) + + self.sp_PageBackBtn = QtWidgets.QPushButton("Back") + self.sp_PageBackBtn.setMinimumHeight(50) + self.sp_PageBackBtn.setFont(font) + self.sp_PageBackBtn.clicked.connect(partial(self.backToMeshIconCreation)) + self.sp_PageBackBtn.setObjectName("blueButton") + + + self.sp_PageContBtn = QtWidgets.QPushButton("BUILD") + self.sp_PageContBtn.setMinimumHeight(50) + self.sp_PageContBtn.setFont(font) + self.sp_PageContBtn.clicked.connect(partial(self.launchBuild)) + self.sp_PageContBtn.setObjectName("blueButton") + + self.sp_PageButtonLayout.addWidget(self.sp_PageBackBtn) + spacer = QtWidgets.QSpacerItem(200, 20) + self.sp_PageButtonLayout.addSpacerItem(spacer) + self.sp_PageButtonLayout.addWidget(self.sp_PageContBtn) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def highQualityToggle(self): + + state = self.hqRenderCB.isChecked() + + if state: + cmds.modelEditor(self.tcViewport, edit = True, rnm = "ogsRenderer") + + if not state: + cmds.modelEditor(self.tcViewport, edit = True, rnm = "base_OpenGL_Renderer") + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def shadowsToggle(self, lights): + + state = self.shadowsCB.isChecked() + + for light in lights: + cmds.setAttr(light + ".useDepthMapShadows", state) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def pitchLights(self, lights, slider, *args): + + value = slider.value() + + for light in lights: + cmds.setAttr(light + ".rotateX", value) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def changeLightIntensity(self, light, dial, *args): + + #get dial value + value = dial.value() + value = float(value)/50.0 + + cmds.setAttr(light + ".intensity", value) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def changeLightColor(self, light, button, *args): + + #launch color dialog + self.qColorDialog = QtWidgets.QColorDialog.getColor() + color = self.qColorDialog.getRgb() + red = float(color[0]) + green = float(color[1]) + blue = float(color[2]) + + cmds.setAttr(light + ".colorR", float(red/255)) + cmds.setAttr(light + ".colorG", float(green/255)) + cmds.setAttr(light + ".colorB", float(blue/255)) + + button.setStyleSheet("color: rgb(" + str(red) + "," + str(green) + "," + str(blue) + ");") + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def orbitLights(self, group, dial, *args): + + value = dial.value() + cmds.setAttr(group + ".rotateZ", value) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def loadCustomImg(self): + + try: + imgPath = cmds.fileDialog2(fm = 1, dir = self.iconsPath)[0] + imgPath = utils.returnFriendlyPath(imgPath) + + #check to make sure image is valid + extension = imgPath.rpartition(".")[2] + if extension != "png": + cmds.warning("Please upload only png files!") + else: + self.customImg.setStyleSheet("background-image: url(" + imgPath + ");") + self.customThumbnail.setText(imgPath) + self.viewportToggle.setCurrentIndex(0) + + except: + cmds.warning("No File Chosen") + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def moduleSelected(self): + #Original Author: Jeremy Ernst + + currentSelection = cmds.ls(sl = True) + + #run toggleMoverVisibility + self.mainUI.setMoverVisibility() + + #clean up current module + if self.currentModule != None: + self.currentModule.cleanUpRigPose() + + #find the currently selected module + selected = self.rpPage_moduleList.selectedItems() + module = selected[0].text() + + #find the network node this module belongs to + networkNode = None + modules = utils.returnRigModules() + for mod in modules: + modName = cmds.getAttr(mod + ".moduleName") + if modName == module: + networkNode = mod + break + + #find the instance in memory of the selected module + if networkNode != None: + + for inst in self.mainUI.moduleInstances: + + if inst.returnNetworkNode == networkNode: + + #call on module's method for unhiding and constraining joints + inst.setupForRigPose() + + #call on the module's getRigPose method + inst.getReferencePose("rigPose") + + #call on the module's rigPose_UI method + index = self.rpPage_stackWidget.indexOf(inst.rigPoseFrame) + self.rpPage_stackWidget.setCurrentIndex(index) + + #set current module + self.currentModule = inst + + cmds.select(clear = True) + if len(currentSelection) > 0: + cmds.select(currentSelection) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateProjects(self): + #Original Author: Jeremy Ernst + + #if the project path doesn't exist on disk, create it + if not os.path.exists(self.projectPath): + os.makedirs(self.projectPath) + + #get a list of the existing folders in projects + existingProjects = os.listdir(self.projectPath) + folders = [] + + #find out which returned items are directories + for each in existingProjects: + if os.path.isdir(os.path.join(self.projectPath, each)): + folders.append(each) + + #if there are no projects, bring up add project interface + if len(folders) == 0: + self.addNewProject(False) + + #otherwise, add each project to the combo box + else: + self.projectComboBox.clear() + for each in folders: + self.projectComboBox.addItem(each) + + + #find selected project and populate groups + self.populateGroups() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateGroups(self): + #Original Author: Jeremy Ernst + + #get a list of the existing folders in projects + selectedProject = self.projectComboBox.currentText() + project = os.path.join(self.projectPath, selectedProject) + existingGroups = os.listdir(project) + folders = [] + + #find out which returned items are directories + for each in existingGroups: + if os.path.isdir(os.path.join(project, each)): + folders.append(each) + + #otherwise, add each project to the combo box + self.groupComboBox.clear() + self.groupComboBox.addItem(" ") + for each in folders: + self.groupComboBox.addItem(each) + + + #populate characters + self.populateCharacters() +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateCharacters(self): + #Original Author: Jeremy Ernst + + #get a list of the existing folders in projects + selectedProject = self.projectComboBox.currentText() + fullPath = os.path.join(self.projectPath, selectedProject) + selectedGroup = self.groupComboBox.currentText() + if len(selectedGroup) > 1: + fullPath = os.path.join(fullPath, selectedGroup) + + existingCharacters = os.listdir(fullPath) + files = [] + + #find out which returned items are directories + for each in existingCharacters: + if os.path.isfile(os.path.join(fullPath, each)): + if each.rpartition(".")[2] == "ma": + files.append(each) + + #otherwise, add each project to the combo box + self.characterList.clear() + + + for each in files: + item = QtWidgets.QListWidgetItem(each.partition(".ma")[0]) + self.characterList.addItem(item) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addNewProject(self, fromButton): + #Original Author: Jeremy Ernst + + #simple UI for user to add new Project + if cmds.window("ART_Publish_AddNewProjUI", exists = True): + cmds.deleteUI("ART_Publish_AddNewProjUI", wnd = True) + + #launch a UI to get the name information + self.addNewProjWindow = QtWidgets.QMainWindow(self.mainWin) + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.addNewProjWindow_mainWidget = QtWidgets.QWidget() + self.addNewProjWindow.setCentralWidget(self.addNewProjWindow_mainWidget) + + #set qt object name + self.addNewProjWindow.setObjectName("ART_Publish_AddNewProjUI") + self.addNewProjWindow.setWindowTitle("Add New Project") + + #create the mainLayout for the rig creator UI + self.addNewProjWindow_mainLayout = QtWidgets.QVBoxLayout(self.addNewProjWindow_mainWidget) + self.addNewProjWindow_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.addNewProjWindow.resize(300, 100) + self.addNewProjWindow.setSizePolicy(mainSizePolicy) + self.addNewProjWindow.setMinimumSize(QtCore.QSize( 300, 100 )) + self.addNewProjWindow.setMaximumSize(QtCore.QSize( 300, 100 )) + + #add stackWidget for ability to swap between two different pages (page 1: No projects exist. page 2: add new project) + self.addNewProjWindow_stackWidget = QtWidgets.QStackedWidget() + self.addNewProjWindow_mainLayout.addWidget(self.addNewProjWindow_stackWidget) + + + #NO PROJECT EXISTS PAGE + + #add background image/QFrame for first page + page1Widget = QtWidgets.QWidget() + self.addNewProjWindow_stackWidget.addWidget(page1Widget) + + self.addNewProjWindow_frame = QtWidgets.QFrame(page1Widget) + self.addNewProjWindow_frame.setMinimumSize(QtCore.QSize( 300, 100 )) + self.addNewProjWindow_frame.setMaximumSize(QtCore.QSize( 300, 100 )) + self.addNewProjWindow_frame.setStyleSheet("background-image: url(" + self.imageBkgrd + ");") + + #Add simple VBoxLayout for page + self.addNewProjWindow_page1Layout = QtWidgets.QVBoxLayout(self.addNewProjWindow_frame) + + #add label + label = QtWidgets.QLabel("No projects exist in this directory. Would you like to add one now?") + label.setWordWrap(True) + label.setAlignment(QtCore.Qt.AlignCenter) + self.addNewProjWindow_page1Layout.addWidget(label) + + #add buttons for Yes/Cancel + self.addNewProjWinPage1_buttonLayout = QtWidgets.QHBoxLayout() + self.addNewProjWindow_page1Layout.addLayout(self.addNewProjWinPage1_buttonLayout) + + self.addNewProjWinPage1_CancelButton = QtWidgets.QPushButton("Cancel") + self.addNewProjWinPage1_buttonLayout.addWidget(self.addNewProjWinPage1_CancelButton) + self.addNewProjWinPage1_CancelButton.setStyleSheet("background-image: url(" + self.imageBtnBkrd + ");background-color: rgb(25, 175, 255);") + self.addNewProjWinPage1_CancelButton.clicked.connect(self.addNewProjWindow.close) + + + self.addNewProjWinPage1_YesButton = QtWidgets.QPushButton("Yes") + self.addNewProjWinPage1_buttonLayout.addWidget(self.addNewProjWinPage1_YesButton) + self.addNewProjWinPage1_YesButton.setStyleSheet("background-image: url(" + self.imageBtnBkrd + ");background-color: rgb(25, 175, 255);") + self.addNewProjWinPage1_YesButton.clicked.connect(partial(self.addNewProjWindow_stackWidget.setCurrentIndex,1)) + + + + #ADD NEW PROJECT PAGE + + #add background image/QFrame for first page + page2Widget = QtWidgets.QWidget() + self.addNewProjWindow_stackWidget.addWidget(page2Widget) + + self.addNewProjWinPage2_frame = QtWidgets.QFrame(page2Widget) + self.addNewProjWinPage2_frame.setMinimumSize(QtCore.QSize( 300, 100 )) + self.addNewProjWinPage2_frame.setMaximumSize(QtCore.QSize( 300, 100 )) + self.addNewProjWinPage2_frame.setStyleSheet("background-image: url(" + self.imageBkgrd + ");") + + #Add simple VBoxLayout for page + self.addNewProjWindow_page2Layout = QtWidgets.QVBoxLayout(self.addNewProjWinPage2_frame) + + #line edit for writing project name + self.addNewProjLineEdit = QtWidgets.QLineEdit() + self.addNewProjLineEdit.setPlaceholderText("Project Name") + self.addNewProjWindow_page2Layout.addWidget(self.addNewProjLineEdit) + self.addNewProjLineEdit.setStyleSheet("background-image: url(" + self.frameBackground + ");") + + + #add project button + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + self.addNewProj_AddButton = QtWidgets.QPushButton("Create New Project") + self.addNewProjWindow_page2Layout.addWidget(self.addNewProj_AddButton) + self.addNewProj_AddButton.setFont(font) + self.addNewProj_AddButton.setStyleSheet("background-image: url(" + self.imageBtnBkrd + ");background-color: rgb(25, 175, 255);") + self.addNewProj_AddButton.clicked.connect(self.createNewProject) + + #show the ui + self.addNewProjWindow.show() + + if not fromButton: + self.addNewProjWindow_stackWidget.setCurrentIndex(0) + if fromButton: + self.addNewProjWindow_stackWidget.setCurrentIndex(1) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addNewGroup(self): + #Original Author: Jeremy Ernst + + #simple UI for user to add new Group + if cmds.window("ART_Publish_AddNewGrpUI", exists = True): + cmds.deleteUI("ART_Publish_AddNewGrpUI", wnd = True) + + #launch a UI to get the name information + self.addNewGrpWin = QtWidgets.QMainWindow(self.mainWin) + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.addNewGrpWin_mainWidget = QtWidgets.QWidget() + self.addNewGrpWin.setCentralWidget(self.addNewGrpWin_mainWidget) + + #set qt object name + self.addNewGrpWin.setObjectName("ART_Publish_AddNewGrpUI") + self.addNewGrpWin.setWindowTitle("Add New Group") + + #create the mainLayout for the rig creator UI + self.addNewGrpWin_mainLayout = QtWidgets.QVBoxLayout(self.addNewGrpWin_mainWidget) + self.addNewGrpWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.addNewGrpWin.resize(300, 100) + self.addNewGrpWin.setSizePolicy(mainSizePolicy) + self.addNewGrpWin.setMinimumSize(QtCore.QSize( 300, 100 )) + self.addNewGrpWin.setMaximumSize(QtCore.QSize( 300, 100 )) + + + #add background image/QFrame for first page + self.addNewGrpWin_frame = QtWidgets.QFrame() + self.addNewGrpWin_mainLayout.addWidget(self.addNewGrpWin_frame) + self.addNewGrpWin_frame.setMinimumSize(QtCore.QSize( 300, 100 )) + self.addNewGrpWin_frame.setMaximumSize(QtCore.QSize( 300, 100 )) + self.addNewGrpWin_frame.setStyleSheet("background-image: url(" + self.imageBkgrd + ");") + + #create vertical layout + self.addNewGrpWin_vLayout = QtWidgets.QVBoxLayout(self.addNewGrpWin_frame) + + #line edit for writing group name + self.addNewGrpWinLineEdit = QtWidgets.QLineEdit() + self.addNewGrpWinLineEdit.setPlaceholderText("Group Name") + self.addNewGrpWin_vLayout.addWidget(self.addNewGrpWinLineEdit) + self.addNewGrpWinLineEdit.setStyleSheet("background-image: url(" + self.frameBackground + ");") + + + #add group button + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + + self.addNewGrpWin_AddButton = QtWidgets.QPushButton("Create New Group") + self.addNewGrpWin_vLayout.addWidget(self.addNewGrpWin_AddButton) + self.addNewGrpWin_AddButton.setFont(font) + self.addNewGrpWin_AddButton.setStyleSheet("background-image: url(" + self.imageBtnBkrd + ");background-color: rgb(25, 175, 255);") + self.addNewGrpWin_AddButton.clicked.connect(self.createNewGroup) + + #show the ui + self.addNewGrpWin.show() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def setCharacterName(self): + #Original Author: Jeremy Ernst + + selected = self.characterList.currentItem().text() + self.characterName.setText(selected) +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createNewProject(self): + #Original Author: Jeremy Ernst + + #get name from lineEdit + projectName = self.addNewProjLineEdit.text() + if len(projectName) == 0: + cmds.warning("No valid Project Name entered.") + return + + #make sure there are no naming conflicts + existingProjects = os.listdir(self.projectPath) + if projectName in existingProjects: + cmds.warning("Project with that name already exists. Aborting..") + return + + #make the directory + path = os.path.join(self.projectPath, projectName) + os.makedirs(path) + + #close the ui + self.addNewProjWindow.close() + + #repopulate + self.populateProjects() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createNewGroup(self): + #Original Author: Jeremy Ernst + + #get name from lineEdit + groupName = self.addNewGrpWinLineEdit.text() + if len(groupName) == 0: + cmds.warning("No valid Group Name entered.") + return + + #make sure there are no naming conflicts + selectedProject = self.projectComboBox.currentText() + project = os.path.join(self.projectPath, selectedProject) + existingGroups = os.listdir(project) + + if groupName in existingGroups: + cmds.warning("Group with that name already exists. Aborting..") + return + + #make the directory + path = os.path.join(project, groupName) + os.makedirs(path) + + #close the ui + self.addNewGrpWin.close() + + #repopulate + self.populateGroups() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createNewCharacter(self): + #Original Author: Jeremy Ernst + + #get character name from lineEdit + characterName = self.characterName.text() + toContinue = False + + #get existing names in characterList + existingCharacters = [] + for i in range(self.characterList.count()): + existingCharacters.append(self.characterList.item(i).text()) + + #check if name is unique. If not, ask if user wants to overwrite + if characterName not in existingCharacters: + toContinue = True + + else: + toContinue = self.overwriteCharacterUI() + + if toContinue: + + #if the name is not an empty string, proceed. + if len(characterName) > 0: + + #check for attributes on ART_RIG_ROOT node + characterNode = utils.returnCharacterModule() + firstVersion = False + + if cmds.objExists(characterNode + ".project") == False: + cmds.addAttr(characterNode, ln = "project", dt = "string", keyable = True) + + if cmds.objExists(characterNode + ".group") == False: + cmds.addAttr(characterNode, ln = "group", dt = "string", keyable = True) + + if cmds.objExists(characterNode + ".name") == False: + cmds.addAttr(characterNode, ln = "name", dt = "string", keyable = True) + + if cmds.objExists(characterNode + ".version") == False: + cmds.addAttr(characterNode, ln = "version", keyable = True) + firstVersion = True + + if cmds.objExists(characterNode + ".versionNote") == False: + cmds.addAttr(characterNode, ln = "versionNote", dt = "string", keyable = True) + + if cmds.objExists(characterNode + ".publishedBy") == False: + cmds.addAttr(characterNode, ln = "publishedBy", dt = "string", keyable = True) + + + #get data from UI + projectName = self.projectComboBox.currentText() + groupName = self.groupComboBox.currentText() + charName = self.characterName.text() + + #get user + for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): + user = os.environ.get(name) + cmds.setAttr(characterNode + ".publishedBy", user, type = "string") + + + #version character + if firstVersion: + cmds.setAttr(characterNode + ".version", 1) + cmds.setAttr(characterNode + ".versionNote", json.dumps([[1, "initial checkin", cmds.getAttr(characterNode + ".publishedBy")]]), type = "string") + + #if this is not the firstVersion, check to see if it's technically being saved as a new file + if not firstVersion: + #get current data off node + currentProj = cmds.getAttr(characterNode + ".project") + currentGrp = cmds.getAttr(characterNode + ".group") + currentName = cmds.getAttr(characterNode + ".name") + + if type(currentGrp) == type(None): + currentGrp = " " + + + #figure out if this is truly a new version, or if this is getting published as something else + doVersion = False + if projectName == currentProj: + if groupName == currentGrp: + if charName == currentName: + doVersion = True + + if doVersion: + currentVersion = cmds.getAttr(characterNode + ".version") + + #find type of revision + changeType = self.changeType.currentText() + if changeType == "Cosmetic Change": + value = .01 + if changeType == "Minor Change": + value = .1 + if changeType == "Major Change": + value = 1 + + cmds.setAttr(characterNode + ".version", currentVersion + value) + + else: + cmds.setAttr(characterNode + ".version", 1) + cmds.setAttr(characterNode + ".versionNote", json.dumps([1, "initial checkin", cmds.getAttr(characterNode + ".publishedBy")]), type = "string") + + #version note + string = self.revisionNote.toPlainText() + revisionHistory = json.loads(cmds.getAttr(characterNode + ".versionNote")) + revisionHistory.append([cmds.getAttr(characterNode + ".version"), string, cmds.getAttr(characterNode + ".publishedBy")]) + cmds.setAttr(characterNode + ".versionNote", json.dumps(revisionHistory), type = "string") + + #set data on characterNode + cmds.setAttr(characterNode + ".project", projectName, type = "string") + cmds.setAttr(characterNode + ".group", groupName, type = "string") + cmds.setAttr(characterNode + ".name", charName, type = "string") + + + + #save maya ascii file in path + selectedProject = self.projectComboBox.currentText() + fullPath = os.path.join(self.projectPath, selectedProject) + selectedGroup = self.groupComboBox.currentText() + if len(selectedGroup) > 1: + fullPath = os.path.join(fullPath, selectedGroup) + else: + selectedGroup = None + + #save file + fullPath = utils.returnFriendlyPath(os.path.join(fullPath, charName + ".ma")) + cmds.file(rename = fullPath) + cmds.file(save = True, type = "mayaAscii") + + + #repopulate character list + self.populateCharacters() + + #add info to publishFileInfo + self.publishFileInfo.append([fullPath, selectedProject, selectedGroup, charName]) + + + #go to next page + self.continueToRigPose() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def overwriteCharacterUI(self): + #Original Author: Jeremy Ernst + + #message box for confirming if character should be overwritten or not + msgBox = QtWidgets.QMessageBox() + msgBox.setIcon(QtWidgets.QMessageBox.Warning) + msgBox.setText("A character already exists with the given name!") + msgBox.addButton("Overwrite", QtWidgets.QMessageBox.YesRole) + msgBox.addButton("Cancel", QtWidgets.QMessageBox.NoRole) + ret = msgBox.exec_() + + if ret == 1: + return False + else: + return True + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def overwriteIconUI(self, path): + #Original Author: Jeremy Ernst + + #message box for confirming if icon should be overwritten or not + msgBox = QtWidgets.QMessageBox() + msgBox.setIcon(QtWidgets.QMessageBox.Question) + msgBox.setText("This asset already has an icon associated with it.") + msgBox.addButton("Overwrite", QtWidgets.QMessageBox.YesRole) + msgBox.addButton("Keep Current Icon", QtWidgets.QMessageBox.NoRole) + msgBox.setDetailedText("Current Icon:\n\n" + path) + ret = msgBox.exec_() + + if ret == 1: + return False + else: + return True + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def cancelPublish(self): + #Original Author: Jeremy Ernst + + cmds.deleteUI("ART_PublishWin", wnd = True) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def continueToProject(self): + #Original Author: Jeremy Ernst + + self.stackWidget.setCurrentIndex(1) + + #populate projects + self.populateProjects() + + #populate project page info if it exists on character node + characterNode = utils.returnCharacterModule() + + attrs = cmds.listAttr(characterNode, ud = True) + for attr in attrs: + #set project if applicable + if attr.find("project") == 0: + project = cmds.getAttr(characterNode + ".project") + for i in range(self.projectComboBox.count()): + item = self.projectComboBox.itemText(i) + if item == project: + self.projectComboBox.setCurrentIndex(i) + + #set group if applicable + if attr.find("group") == 0: + group = cmds.getAttr(characterNode + ".group") + for i in range(self.groupComboBox.count()): + item = self.groupComboBox.itemText(i) + if item == group: + self.groupComboBox.setCurrentIndex(i) + + #select character, if applicable + if attr.find("name") == 0: + name = cmds.getAttr(characterNode + ".name") + for i in range(self.characterList.count()): + item = self.characterList.item(i).text() + if item == name: + self.characterList.setCurrentRow(i) + self.setCharacterName() + + #set pre-script, if applicable + if attr.find("preScriptPath") == 0: + path = cmds.getAttr(characterNode + ".preScriptPath") + self.preScriptLineEdit.setText(path) + + #set post-script, if applicable + if attr.find("postScriptPath") == 0: + path = cmds.getAttr(characterNode + ".postScriptPath") + self.postScriptLineEdit.setText(path) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def continueToRigPose(self): + #Original Author: Jeremy Ernst + + self.stackWidget.setCurrentIndex(2) + + self.mainWin.setWindowTitle("Create Rig Pose") + + #if pre/post script slots are empty, and the attrs exist, remove attrs + characterModule = utils.returnCharacterModule() + + if self.preScriptLineEdit.text() == "": + if cmds.objExists(characterModule + ".preScriptPath"): + cmds.deleteAttr(characterModule, at = "preScriptPath") + + if self.postScriptLineEdit.text() == "": + if cmds.objExists(characterModule + ".postScriptPath"): + cmds.deleteAttr(characterModule, at = "postScriptPath") + + + #setup the interfaces + for inst in self.mainUI.moduleInstances: + listItems = [] + for i in range(self.rpPage_moduleList.count()): + + text = self.rpPage_moduleList.item(i).text() + listItems.append(text) + + if inst.name in listItems: + inst.rigPose_UI(self.rpPage_stackWidget) + + #set slider states if attribute exists + networkNode = inst.returnNetworkNode + if cmds.objExists(networkNode + ".rigPoseState"): + sliderData = json.loads(cmds.getAttr(networkNode + ".rigPoseState")) + + + #create progress bar + progBar = QtWidgets.QProgressDialog(self.mainWin) + progBar.setCancelButton(None) + progBar.setStyleSheet(self.style) + progBar.setLabelText("Setting rig pose slider values for " + inst.name) + progBar.setMinimum(0) + + #get the slider children, if there is a match between the name property and the name data in the list, set slider value + sliders = inst.rigPoseFrame.findChildren(QtWidgets.QSlider) + + progBar.setMaximum(len(sliders)) + progBar.show() + + for slider in sliders: + name = slider.property("name") + progBar.setValue(progBar.value() + 1) + + for data in sliderData: + sliderName = data.get("name") + + if sliderName == name: + value = data.get("value") + inst.setupForRigPose() + slider.setValue(value) + if cmds.objExists(networkNode + ".rigPose"): + inst.setReferencePose("rigPose") + inst.cleanUpRigPose() + + progBar.deleteLater() + + + self.rpPage_stackWidget.setCurrentIndex(0) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def continueToCreateAnimMesh(self): + #Original Author: Jeremy Ernst + + #save slider states and rig pose + for inst in self.mainUI.moduleInstances: + if inst.name != "root": + networkNode = inst.returnNetworkNode + inst.cleanUpRigPose() + + #add attr if it doesn't exist + if not cmds.objExists(networkNode + ".rigPoseState"): + cmds.addAttr(networkNode, ln = "rigPoseState", dt = "string") + + #if rig pose doesn't exist, get and set it + inst.getReferencePose("rigPose", False) + + + #get the sliders from this widget (name and value) + sliderList = [] + sliders = inst.rigPoseFrame.findChildren(QtWidgets.QSlider) + for slider in sliders: + sliderData = {} + sliderData["name"] = slider.property("name") + sliderData["value"] = slider.value() + + sliderList.append(sliderData) + + #set the rigPoseState attr + jsonString = json.dumps(sliderList) + cmds.setAttr(networkNode + ".rigPoseState", jsonString, type = "string") + + #get the character node + characterNode = utils.returnCharacterModule() + + #go to next UI page + self.mainWin.setWindowTitle("Create Animation Mesh") + self.stackWidget.setCurrentIndex(3) + self.movie.start() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def skipToCreateThumbnail(self): + #Original Author: Jeremy Ernst + + #get the character node + characterNode = utils.returnCharacterModule() + + #get the icon path if present + if cmds.objExists(characterNode + ".iconPath"): + path = cmds.getAttr(characterNode + ".iconPath") + if os.path.exists(utils.returnNicePath(self.projectPath,path)): + toContinue = self.overwriteIconUI(path) + + # if the user wants to overwrite the existing icon + if toContinue: + + self.stackWidget.setCurrentIndex(4) + self.mainWin.setWindowTitle("Create Thumbnail") + # if the user wants to keep the existing icon + else: + self.continueToSummary(True) + + # if the icon path did not exist on disc + else: + self.stackWidget.setCurrentIndex(4) + self.mainWin.setWindowTitle("Create Thumbnail") + + else: + self.stackWidget.setCurrentIndex(4) + self.mainWin.setWindowTitle("Create Thumbnail") + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def continueToCreateThumbnail(self): + #Original Author: Jeremy Ernst + + #get the character node + characterNode = utils.returnCharacterModule() + + #get asset name + name = cmds.getAttr(characterNode + ".name") + + #get LOD0 meshes + if cmds.objExists(characterNode + ".LOD_0"): + jsonData = json.loads(cmds.getAttr(characterNode + ".LOD_0")) + meshes = jsonData.get("Meshes") + else: + meshes = utils.findAllSkinnableGeo() + + for mesh in meshes: + newMesh = utils.splitMesh(mesh, name) + cmds.select(newMesh) + + #select all new meshes and add to layer if layer doesn't exist + if not cmds.objExists("Animation_Mesh_Geo"): + cmds.createDisplayLayer(newMesh, name = "Animation_Mesh_Geo") + + else: + cmds.editDisplayLayerMembers("Animation_Mesh_Geo", newMesh) + + + #get the icon path if present + cmds.select(clear = True) + if cmds.objExists(characterNode + ".iconPath"): + path = cmds.getAttr(characterNode + ".iconPath") + if os.path.exists(utils.returnNicePath(self.projectPath, path)): + toContinue = self.overwriteIconUI(path) + if toContinue: + + self.stackWidget.setCurrentIndex(5) + self.mainWin.setWindowTitle("Create Thumbnail") + + else: + self.continueToSummary(True) + else: + self.stackWidget.setCurrentIndex(5) + self.mainWin.setWindowTitle("Create Thumbnail") + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def continueToSummary(self, skip): + + + #set window title + self.mainWin.setWindowTitle("Summary") + + #get character attributes to build up the path + characterNode = utils.returnCharacterModule() + project = cmds.getAttr(characterNode + ".project") + group = cmds.getAttr(characterNode + ".group") + name = cmds.getAttr(characterNode + ".name") + version = cmds.getAttr(characterNode + ".version") + + + #create the path + if len(group) > 1: + path = utils.returnFriendlyPath(project + "/" + group + "/" + name + ".png") + else: + path = utils.returnFriendlyPath(project + "/" + name + ".png") + + #add icon attr to character node if needed + if not cmds.objExists(characterNode + ".iconPath"): + cmds.addAttr(characterNode, ln = "iconPath", dt = "string", keyable = True) + + cmds.setAttr(characterNode + ".iconPath", path, type = "string") + + #render our images if needed + if not skip: + if self.viewportToggle.currentIndex() == 1: + + #use API to grab image from view + newView = mui.M3dView() + view = mui.M3dView.getM3dViewFromModelEditor(self.tcViewport, newView) + + #read the color buffer from the view, and save the MImage to disk + image = openMaya.MImage() + newView.readColorBuffer(image, True) + image.writeToFile(utils.returnNicePath(self.projectPath, path), 'png') + + + #if user passed in an image, save it to the appropriate location with the correct extension + else: + customPath = self.customThumbnail.text() + print customPath, path + + import shutil + shutil.copyfile(customPath, utils.returnNicePath(self.projectPath, path)) + + + #switch to summary page + self.stackWidget.setCurrentIndex(5) + + #find if pre/post script added + preScript = False + if cmds.objExists(characterNode + ".preScript"): + preScript = True + + postScript = False + if cmds.objExists(characterNode + ".postScript"): + postScript = True + + #get latest icon + self.sumPageIcon.setStyleSheet("background-image: url(" + utils.returnNicePath(self.projectPath, path) + ");") + + #set info + self.sumPageAssetName.setText(name) + self.sumPageProj.setText(project) + self.sumPageGroup.setText(group) + self.sumPageRevisionType.setText(str(version)) + self.sp_preScriptCB.setChecked(preScript) + self.sp_postScriptCB.setChecked(postScript) + + if cmds.objExists(name + "_animMeshGrp"): + self.sp_animMeshCB.setChecked(True) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def backToMeshIconCreation(self): + + self.stackWidget.setCurrentIndex(4) + self.mainWin.setWindowTitle("Create Thumbnail") + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def backToMeshSlicer(self): + + self.stackWidget.setCurrentIndex(3) + self.mainWin.setWindowTitle("Create Animation Mesh") + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def backToProject(self): + #Original Author: Jeremy Ernst + + self.stackWidget.setCurrentIndex(1) + self.mainWin.setWindowTitle("Publish") + + + #make sure we hide all joint movers when going back and put things in the proper state + for inst in self.mainUI.moduleInstances: + listItems = [] + for i in range(self.rpPage_moduleList.count()): + + text = self.rpPage_moduleList.item(i).text() + listItems.append(text) + + if inst.name in listItems: + inst.cleanUpRigPose() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def backToRigPose(self): + #Original Author: Jeremy Ernst + + self.stackWidget.setCurrentIndex(2) + self.mainWin.setWindowTitle("Create Rig Pose") + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addCustomScripts(self, preScript, postScript): + #Original Author: Jeremy Ernst + + #launch the file dialog window + try: + fileName = cmds.fileDialog2(startingDirectory = cmds.internalVar(usd = True), ff = "*.py;;*.mel", fm = 1, okCaption = "Load Script")[0] + fileName = utils.returnFriendlyPath(fileName) + + except: + return + + #add attributes if they don't exist to character node + characterNode = utils.returnCharacterModule() + + #edit the lineEdit to have the path to the script + if preScript: + self.preScriptLineEdit.setText(fileName) + + if not cmds.objExists(characterNode + ".preScriptPath"): + cmds.addAttr(characterNode, ln = "preScriptPath", dt = "string", keyable = True) + + #set attrs + cmds.setAttr(characterNode + ".preScriptPath", fileName, type = "string") + + + #edit the lineEdit to have the path to the script + if postScript: + self.postScriptLineEdit.setText(fileName) + + if not cmds.objExists(characterNode + ".postScriptPath"): + cmds.addAttr(characterNode, ln = "postScriptPath", dt = "string", keyable = True) + + #set attrs + cmds.setAttr(characterNode + ".postScriptPath", fileName, type = "string") + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def publishHelp(self): + #Original Author: Jeremy Ernst + + import ART_Help + reload(ART_Help) + + helpMovie = utils.returnFriendlyPath(os.path.join(self.iconsPath, "Help/publish.gif")) + helpInst = ART_Help.ART_HelpMovie(self.mainWin, helpMovie) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def rigPoseHelp(self): + #Original Author: Jeremy Ernst + + import ART_Help + reload(ART_Help) + + helpMovie = utils.returnFriendlyPath(os.path.join(self.iconsPath, "Help/rigPose.gif")) + helpInst = ART_Help.ART_HelpMovie(self.mainWin, helpMovie) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def launchBuild(self): + #Original Author: Jeremy Ernst + + if cmds.window("ART_PublishWin", exists = True): + cmds.deleteUI("ART_PublishWin", wnd = True) + + #save the file + cmds.file(save = True) + + #launch build progress UI + import ART_BuildProgressUI + reload(ART_BuildProgressUI) + ART_BuildProgressUI.ART_BuildProgress_UI(self.mainUI) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeWin(self, event): + #Original Author: Jeremy Ernst + + if cmds.window("ART_PublishWin", exists = True): + self.mainWin.close() + + #override the close event for the window with a custom function that makes sure the scene is cleaned up + #delete the light and camera rig for the icon creator + try: + for node in [self.thumbnailCamera, self.lightGrp]: + children = cmds.listRelatives(node, children = True) + if len(children) > 0: + for each in children: + cmds.lockNode(each, lock = False) + cmds.lockNode(node, lock = False) + cmds.delete(node) + except: + pass + + for inst in self.mainUI.moduleInstances: + try: + #call on the module's bakeOffsets method + inst.setupForRigPose() + inst.setReferencePose("modelPose") + inst.cleanUpRigPose() + except Exception, e: + print e + + event.accept() + diff --git a/Core/Scripts/Interfaces/ART_RemoveModuleFromCanvas.py b/Core/Scripts/Interfaces/ART_RemoveModuleFromCanvas.py new file mode 100644 index 0000000..5a4e053 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_RemoveModuleFromCanvas.py @@ -0,0 +1,207 @@ +''' +Created on Aug 10, 2015 + +@author: jeremy +''' + + +#import statements +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +import maya.cmds as cmds +import System.utils as utils +import System.interfaceUtils + + +class ART_RemoveModuleFromCanvas(object): + def __init__(self, animPickerUI, modulesToAdd, parent = None): + + super(ART_RemoveModuleFromCanvas, self).__init__() + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.pickerUI = animPickerUI + self.modules = [] + self.modulesToAdd = modulesToAdd + + #assign close event + self.closeEvent = self.closeWin + + #build the UI + self.buildUI() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + + + if cmds.window("pyART_AddToCanvasWIN", exists = True): + cmds.deleteUI("pyART_AddToCanvasWIN", wnd = True) + + #create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + #create the mainLayout + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + + self.mainWin.setStyleSheet(self.style) + + + self.mainWin.setMinimumSize(QtCore.QSize( 250, 400 )) + self.mainWin.setMaximumSize(QtCore.QSize( 250, 400 )) + self.mainWin.resize(250, 400) + + + #set qt object name + self.mainWin.setObjectName("pyART_AddToCanvasWIN") + self.mainWin.setWindowTitle("Remove Module From Canvas") + + + #label, listWidget, button + label = QtWidgets.QLabel("Available Modules:") + label.setProperty("boldFont", True) + self.layout.addWidget(label) + + self.moduleList = QtWidgets.QListWidget() + self.moduleList.setMaximumSize(230, 300) + self.moduleList.setMinimumSize(230, 300) + self.layout.addWidget(self.moduleList) + + #add modules to listWidget + self.addModulesToList() + + #create remove button + button = QtWidgets.QPushButton("Remove From Canvas") + self.layout.addWidget(button) + button.setObjectName("blueButton") + button.clicked.connect(self.removeFromCanvas) + + + #show ui + self.mainWin.show() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addModulesToList(self): + + #get the current tab index and the widget + index = self.pickerUI.characterTabs.currentIndex() + character = self.pickerUI.characterTabs.tabToolTip(index) + + #find character nodes in the scene, and compare namespace to selected tab + characterMods = utils.returnCharacterModules() + nodeNamespace = "" + + for each in characterMods: + if cmds.objExists(each + ".namespace"): + namespace = cmds.getAttr(each + ".namespace") + if namespace == character: + nodeNamespace = namespace + ":" + + + for module in self.modulesToAdd: + info = self.getCurrentCanvasTab(module) + if info[1] != None: + modName = cmds.getAttr(nodeNamespace + module + ".moduleName") + + #add to listWIdget + item = QtWidgets.QListWidgetItem(modName) + item.setData(QtCore.Qt.UserRole, module) + self.moduleList.addItem(item) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def removeFromCanvas(self): + + selected = self.moduleList.currentItem() + try: + module = selected.data(QtCore.Qt.UserRole) + + data = self.getCurrentCanvasTab(module) + scene = data[0] + item = data[1] + scriptJob = item.data(5) + scene.removeItem(item) + cmds.scriptJob(kill = scriptJob) + + row = self.moduleList.row(selected) + self.moduleList.takeItem(row) + + except: + pass + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getCurrentCanvasTab(self, module): + + #get the current tab index and the widget + index = self.pickerUI.characterTabs.currentIndex() + widget = self.pickerUI.characterTabs.widget(index) + pickerRoot = None + + children = widget.children() + for child in children: + if type(child) == QtWidgets.QTabWidget: + tab = child + + canvasIndex = tab.currentIndex() + canvasWidget = tab.widget(canvasIndex) + + canvasChildren = canvasWidget.children() + for canvasChild in canvasChildren: + if type(canvasChild) == QtWidgets.QGraphicsView: + view = canvasChild + scene = view.scene() + + #get all items in the gfxScene + itemsInScene = scene.items() + + for item in itemsInScene: + if type(item) == System.interfaceUtils.pickerBorderItem or item.type() == 3: + data = item.data(QtCore.Qt.UserRole) + if data == module: + pickerRoot = item + + + return [scene, pickerRoot] + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeWin(self, event): + + + cmds.deleteUI("pyART_AddToCanvasWIN", wnd = True) diff --git a/Core/Scripts/Interfaces/ART_ResetModeUI.py b/Core/Scripts/Interfaces/ART_ResetModeUI.py new file mode 100644 index 0000000..d48f3d8 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ResetModeUI.py @@ -0,0 +1,262 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import System.utils as utils + + + +class ART_ResetMode(): + + def __init__(self, mainUI): + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + #build the UI + self.buildResetModeUI(mainUI) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildResetModeUI(self, mainUI): + + if cmds.window("ART_ResetModeWin", exists = True): + cmds.deleteUI("ART_ResetModeWin", wnd = True) + + #launch a UI to get the name information + self.resetModeWin = QtWidgets.QMainWindow(mainUI) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.resetModeWin.setStyleSheet(self.style) + + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.resetModeWin_mainWidget = QtWidgets.QWidget() + self.resetModeWin.setCentralWidget(self.resetModeWin_mainWidget) + + #set qt object name + self.resetModeWin.setObjectName("ART_ResetModeWin") + self.resetModeWin.setWindowTitle("Reset Modules") + + #create the mainLayout for the rig creator UI + self.resetModeWin_mainLayout = QtWidgets.QVBoxLayout(self.resetModeWin_mainWidget) + self.resetModeWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.resetModeWin.resize(400, 250) + self.resetModeWin.setSizePolicy(mainSizePolicy) + self.resetModeWin.setMinimumSize(QtCore.QSize( 400, 250 )) + self.resetModeWin.setMaximumSize(QtCore.QSize( 400, 250 )) + + #create the background image + self.resetModeWin_frame = QtWidgets.QFrame() + self.resetModeWin_mainLayout.addWidget(self.resetModeWin_frame) + + #create the layout for the widgets + self.resetModeWin_widgetLayout = QtWidgets.QHBoxLayout(self.resetModeWin_frame) + self.resetModeWin_widgetLayout.setContentsMargins(5, 5, 5, 5) + + #add the QListWidget Frame + self.resetModeWin_moduleListFrame = QtWidgets.QFrame() + self.resetModeWin_moduleListFrame.setMinimumSize(QtCore.QSize( 275, 200 )) + self.resetModeWin_moduleListFrame.setMaximumSize(QtCore.QSize( 275, 200 )) + self.resetModeWin_moduleListFrame.setContentsMargins(20,0,20,0) + + #create the list widget + self.resetModeWin_moduleList = QtWidgets.QListWidget(self.resetModeWin_moduleListFrame) + self.resetModeWin_widgetLayout.addWidget(self.resetModeWin_moduleListFrame) + self.resetModeWin_moduleList.setMinimumSize(QtCore.QSize( 265, 200 )) + self.resetModeWin_moduleList.setMaximumSize(QtCore.QSize( 265, 200 )) + self.resetModeWin_moduleList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + self.resetModeWin_moduleList.setSpacing(3) + + #add the layout for the buttons + self.resetModeWin_buttonLayoutAll = QtWidgets.QVBoxLayout() + self.resetModeWin_widgetLayout.addLayout(self.resetModeWin_buttonLayoutAll) + self.resetModeWin_buttonLayoutAll.setContentsMargins(5, 20, 5, 20) + + #button background image + image = utils.returnNicePath(self.iconsPath, "System/blue_field_background.png") + + + #add the selection buttons + self.resetModeWin_selectionButtonLayout = QtWidgets.QVBoxLayout() + self.resetModeWin_buttonLayoutAll.addLayout(self.resetModeWin_selectionButtonLayout) + self.resetModeWin_selectAllButton = QtWidgets.QPushButton("Select All") + self.resetModeWin_selectAllButton.setMinimumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_selectAllButton.setMaximumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_selectionButtonLayout.addWidget(self.resetModeWin_selectAllButton) + self.resetModeWin_selectAllButton.clicked.connect(self.resetModeWin_moduleList.selectAll) + self.resetModeWin_selectAllButton.setObjectName("blueButton") + + self.resetModeWin_selectNoneButton = QtWidgets.QPushButton("Clear Selection") + self.resetModeWin_selectNoneButton.setMinimumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_selectNoneButton.setMaximumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_selectionButtonLayout.addWidget(self.resetModeWin_selectNoneButton) + self.resetModeWin_selectNoneButton.clicked.connect(self.resetModeWin_moduleList.clearSelection) + self.resetModeWin_selectNoneButton.setObjectName("blueButton") + + #spacer + spacerItem = QtWidgets.QSpacerItem(20, 80, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.resetModeWin_selectionButtonLayout.addItem(spacerItem) + + #add the buttons for reset settings and reset transforms + self.resetModeWin_resetSettings = QtWidgets.QPushButton("Reset Settings") + self.resetModeWin_resetSettings.setMinimumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_resetSettings.setMaximumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_selectionButtonLayout.addWidget(self.resetModeWin_resetSettings) + self.resetModeWin_resetSettings.clicked.connect(partial(self.resetMode_resetSettings)) + self.resetModeWin_resetSettings.setObjectName("blueButton") + + self.resetModeWin_resetXforms = QtWidgets.QPushButton("Reset Xforms") + self.resetModeWin_resetXforms.setMinimumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_resetXforms.setMaximumSize(QtCore.QSize( 115, 25 )) + self.resetModeWin_selectionButtonLayout.addWidget(self.resetModeWin_resetXforms) + self.resetModeWin_resetXforms.clicked.connect(partial(self.resetMode_resetXformsUI)) + self.resetModeWin_resetXforms.setObjectName("blueButton") + + #populate the list widget + modules = utils.returnRigModules() + for module in modules: + moduleName = cmds.getAttr(module + ".moduleName") + self.resetModeWin_moduleList.addItem(moduleName) + + + #show the window + self.resetModeWin.show() +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def resetMode_resetSettings(self): + + selected = self.resetModeWin_moduleList.selectedItems() + items = [] + for each in selected: + items.append(each.text()) + for each in self.mainUI.moduleInstances: + name = each.name + if name in items: + each.resetSettings() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def resetMode_resetXformsUI(self): + + #if nothing in the list is selected, return + if self.resetModeWin_moduleList.selectedItems() == []: + return + + #if window exists, delete first + if cmds.window("ART_ResetXformsModeWin", exists = True): + cmds.deleteUI("ART_ResetXformsModeWin", wnd = True) + + #launch a UI to get the name information + self.resetXformsWin = QtWidgets.QMainWindow(self.mainUI) + + self.resetXformsWin.setStyleSheet(self.style) + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.resetXformsWin_mainWidget = QtWidgets.QWidget() + self.resetXformsWin.setCentralWidget(self.resetXformsWin_mainWidget) + + #set qt object name + self.resetXformsWin.setObjectName("ART_ResetXformsModeWin") + self.resetXformsWin.setWindowTitle("Reset Transformations") + + #create the mainLayout for the rig creator UI + self.resetXformsWin_mainLayout = QtWidgets.QVBoxLayout(self.resetXformsWin_mainWidget) + self.resetXformsWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.resetXformsWin.resize(300, 100) + self.resetXformsWin.setSizePolicy(mainSizePolicy) + self.resetXformsWin.setMinimumSize(QtCore.QSize( 300, 100 )) + self.resetXformsWin.setMaximumSize(QtCore.QSize( 300, 100 )) + + #create the background + self.resetXformsWin_frame = QtWidgets.QFrame() + self.resetXformsWin_mainLayout.addWidget(self.resetXformsWin_frame) + + + #create the layout for the widgets + self.resetXformsWin_widgetLayout = QtWidgets.QVBoxLayout(self.resetXformsWin_frame) + self.resetXformsWin_widgetLayout.setContentsMargins(5, 5, 5, 5) + self.resetXformsWin_widgetLayoutRow = QtWidgets.QHBoxLayout(self.resetXformsWin_frame) + self.resetXformsWin_widgetLayout.addLayout(self.resetXformsWin_widgetLayoutRow) + + #add the 3 buttons for translate, rotate, scale + + + + self.resetXformsWin_transCB = QtWidgets.QPushButton("Translate") + self.resetXformsWin_widgetLayoutRow.addWidget(self.resetXformsWin_transCB) + self.resetXformsWin_transCB.setCheckable(True) + self.resetXformsWin_transCB.setChecked(True) + + self.resetXformsWin_rotCB = QtWidgets.QPushButton("Rotate") + self.resetXformsWin_widgetLayoutRow.addWidget(self.resetXformsWin_rotCB) + self.resetXformsWin_rotCB.setCheckable(True) + self.resetXformsWin_rotCB.setChecked(True) + + self.resetXformsWin_scaleCB = QtWidgets.QPushButton("Scale") + self.resetXformsWin_widgetLayoutRow.addWidget(self.resetXformsWin_scaleCB) + self.resetXformsWin_scaleCB.setCheckable(True) + self.resetXformsWin_scaleCB.setChecked(True) + + + + #Create the Reset Transforms button + self.resetXformsWin_resetXformButton = QtWidgets.QPushButton("Reset Transformations") + self.resetXformsWin_widgetLayout.addWidget(self.resetXformsWin_resetXformButton) + self.resetXformsWin_resetXformButton.setMinimumSize(QtCore.QSize( 290, 40 )) + self.resetXformsWin_resetXformButton.setMaximumSize(QtCore.QSize( 290, 40 )) + self.resetXformsWin_resetXformButton.clicked.connect(self.resetMode_resetXforms) + self.resetXformsWin_resetXformButton.setProperty("boldFont", True ) + + #show window + self.resetXformsWin.show() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def resetMode_resetXforms(self): + + translate = self.resetXformsWin_transCB.isChecked() + rotate = self.resetXformsWin_rotCB.isChecked() + scale = self.resetXformsWin_scaleCB.isChecked() + + + selected = self.resetModeWin_moduleList.selectedItems() + items = [] + for each in selected: + items.append(each.text()) + for each in self.mainUI.moduleInstances: + name = each.name + if name in items: + each.resetTransforms(translate, rotate, scale, name)
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/ART_ResetModulesUI.py b/Core/Scripts/Interfaces/ART_ResetModulesUI.py new file mode 100644 index 0000000..df98a79 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_ResetModulesUI.py @@ -0,0 +1,248 @@ +__author__ = 'jeremy.ernst' + +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import os +import System.utils as utils +import System.interfaceUtils as interfaceUtils + + + + + + +class ART_ResetModules(object): + def __init__(self, animPickerUI, parent = None): + + super(ART_ResetModules, self).__init__() + + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.pickerUI = animPickerUI + + #write out qss based on user settings + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir,sheet)) + + #build the UI + self.buildUI() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + + + if cmds.window("pyART_ResetModulesWIN", exists = True): + cmds.deleteUI("pyART_ResetModulesWIN", wnd = True) + + #create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + + #create the main widget + self.mainWidget = QtWidgets.QFrame() + self.mainWidget.setObjectName("dark") + self.mainWin.setCentralWidget(self.mainWidget) + + #create the mainLayout + self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + + self.mainWin.setStyleSheet(self.style) + + + self.mainWin.setMinimumSize(QtCore.QSize( 400, 250 )) + self.mainWin.setMaximumSize(QtCore.QSize( 400, 250 )) + self.mainWin.resize(400, 250) + + + #set qt object name + self.mainWin.setObjectName("pyART_ResetModulesWIN") + self.mainWin.setWindowTitle("Reset Rig Controls") + + + self.layout = QtWidgets.QHBoxLayout() + self.mainLayout.addLayout(self.layout) + + #LEFT SIDE + #list of modules + self.moduleList = QtWidgets.QListWidget() + self.moduleList.setMinimumSize(QtCore.QSize(180, 230)) + self.moduleList.setMaximumSize(QtCore.QSize(180, 230)) + self.layout.addWidget(self.moduleList) + self.moduleList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + + #RIGHT SIDE + self.rightLayout = QtWidgets.QVBoxLayout() + self.layout.addLayout(self.rightLayout) + + + #character combo + self.characterCombo = QtWidgets.QComboBox() + self.rightLayout.addWidget(self.characterCombo) + self.characterCombo.setMinimumSize(QtCore.QSize(180, 60)) + self.characterCombo.setMaximumSize(QtCore.QSize(180, 60)) + self.characterCombo.setIconSize(QtCore.QSize(50,50)) + self.characterCombo.currentIndexChanged.connect(partial(self.findCharacterModules)) + + #buttons + self.selAllBtn = QtWidgets.QPushButton("Select All") + self.selAllBtn.setMinimumWidth(180) + self.selAllBtn.setMaximumWidth(180) + self.rightLayout.addWidget(self.selAllBtn) + self.selAllBtn.setObjectName("blueButton") + self.selAllBtn.clicked.connect(partial(self.selectAllModules)) + + self.clearSelBtn = QtWidgets.QPushButton("Clear Selection") + self.clearSelBtn.setMinimumWidth(180) + self.clearSelBtn.setMaximumWidth(180) + self.rightLayout.addWidget(self.clearSelBtn) + self.clearSelBtn.setObjectName("blueButton") + self.clearSelBtn.clicked.connect(partial(self.clearModuleSelection)) + + self.rightLayout.addSpacerItem(QtWidgets.QSpacerItem(0,0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + #reset transforms button + self.resetXformsBtn = QtWidgets.QPushButton("Reset Transformations") + self.resetXformsBtn.setMinimumWidth(180) + self.resetXformsBtn.setMaximumWidth(180) + self.rightLayout.addWidget(self.resetXformsBtn) + self.resetXformsBtn.setObjectName("blueButton") + self.resetXformsBtn.clicked.connect(partial(self.reset)) + + + + #show window + self.mainWin.show() + + #populate UI + self.findCharacters() + self.findCharacterModules() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacters(self): + + allNodes= cmds.ls(type = "network") + characterNodes = [] + for node in allNodes: + attrs = cmds.listAttr(node) + if "rigModules" in attrs: + characterNodes.append(node) + + #go through each node, find the character name, the namespace on the node, and the picker attribute + for node in characterNodes: + try: + namespace = cmds.getAttr(node + ".namespace") + except: + namespace = cmds.getAttr(node + ".name") + + + #add the icon found on the node's icon path attribute to the tab + iconPath = cmds.getAttr(node + ".iconPath") + iconPath = utils.returnNicePath(self.projectPath, iconPath) + icon = QtGui.QIcon(iconPath) + + + self.characterCombo.addItem(icon, namespace) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacterModules(self, *args): + + self.moduleList.clear() + + #current character + selectedChar = self.characterCombo.currentText() + + #get rig modules + if cmds.objExists(selectedChar + ":" + "ART_RIG_ROOT"): + modules = cmds.listConnections(selectedChar + ":" + "ART_RIG_ROOT.rigModules") + + for module in modules: + modName = cmds.getAttr(module + ".moduleName") + item = QtWidgets.QListWidgetItem(modName) + item.setData(QtCore.Qt.UserRole, module) + self.moduleList.addItem(item) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def selectAllModules(self, *args): + + self.moduleList.selectAll() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def clearModuleSelection(self, *args): + + self.moduleList.clearSelection() + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def reset(self, *args): + + selected = self.moduleList.selectedItems() + selectedChar = self.characterCombo.currentText() + + + for each in selected: + module = each.data(QtCore.Qt.UserRole) + + #get inst + modType = cmds.getAttr(module + ".moduleType") + modName = cmds.getAttr(module + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(mod) + + #get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + #find the instance of that module + moduleInst = moduleClass(self, modName) + + #set namespace for instance + moduleInst.namespace = selectedChar + ":" + + moduleInst.resetRigControls(True)
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/ART_RigCreatorUI.py b/Core/Scripts/Interfaces/ART_RigCreatorUI.py new file mode 100644 index 0000000..65a8d67 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_RigCreatorUI.py @@ -0,0 +1,1819 @@ +# standard imports +import json +import os +from functools import partial + +import maya.OpenMayaUI as mui +import maya.cmds as cmds + +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + +# maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + +# external imports +import System.utils as utils +import System.riggingUtils as riggingUtils +from System.ART_RigModule import ART_RigModule +from RigModules.ART_Root import ART_Root +import System.interfaceUtils as interfaceUtils + +windowTitle = "Rig_Creator" +windowObject = "pyArtRigCreatorUi" + + +class ART_RigCreator_UI(QtWidgets.QMainWindow): + # Original Author: Jeremy Ernst + + def __init__(self, parent=None): + + super(ART_RigCreator_UI, self).__init__(parent) + + # clean up script jobs + jobs = cmds.scriptJob(lj=True) + for job in jobs: + if job.find("ART_") != -1: + jobNum = int(job.partition(":")[0]) + cmds.scriptJob(kill=jobNum, force=True) + + # get settings + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.scriptPath = settings.value("scriptPath") + self.iconsPath = settings.value("iconPath") + + # create a few lists/dicts to store info + self.outlinerWidgets = {} + self.outlinerControls = [] + + # this list will store module instances created by this UI or by any sub-UIs (like addModuleUI) + self.moduleInstances = [] + self.scriptJobs = [] + self.boneCounterInst = None + + # build the UI + self.buildUI() + + # tooltip style sheet + # write out qss based on user settings + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir, sheet)) + + # check to see if the root of our rig network exists + exists = False + networkNodes = cmds.ls(type="network") + for node in networkNodes: + attrs = cmds.listAttr(node) + + if "rigModules" in attrs: + exists = True + + # if the root of the rig network did not exist, create it now as well as our root module + + if exists == False: + + self.rigRootMod = ART_RigModule("ART_RIG_ROOT", None, None) + self.rigRootMod.buildNetwork() + + # create a root module + self.rootMod = ART_Root(self, "root") + self.rootMod.buildNetwork() + self.rootMod.skeletonSettings_UI("Root") + self.rootMod.jointMover_Build("Core/JointMover/ART_Root.ma") + self.rootMod.addJointMoverToOutliner() + self.moduleInstances.append(self.rootMod) + + + + # if module node network existed, create UI elements and do not create any new network nodes + else: + modules = utils.returnRigModules() + + for module in modules: + modType = cmds.getAttr(module + ".moduleType") + modName = cmds.getAttr(module + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(mod) + + # get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + # find the instance of that module and call on the skeletonSettings_UI function + moduleInst = moduleClass(self, modName) + moduleInst.skeletonSettings_UI(modName) + moduleInst.addJointMoverToOutliner() + + self.moduleInstances.append(moduleInst) + + # run toggleMoverVisibility + self.setMoverVisibility() + + # clear selection + cmds.select(clear=True) + + # unisolate selection if needed + try: + isoPnl = cmds.getPanel(wf=True) + isoCrnt = cmds.isolateSelect(isoPnl, q=True, s=True) + if isoCrnt: + cmds.isolateSelect(isoPnl, s=False) + except: + pass + + utils.fitViewAndShade() + + # set the UI to the correct state + if cmds.objExists("ART_RIG_ROOT.state"): + state = cmds.getAttr("ART_RIG_ROOT.state") + if state == 1: + self.toolModeStack.setCurrentIndex(1) + + # remove outliner scriptJobs + for job in self.scriptJobs: + cmds.scriptJob(kill=job, force=True) + + # build the scriptJob for the weight table + self.scriptJobs.append(self.skinToolsInst.weightTable_scriptJob()) + + if state == 2: + self.toolModeStack.setCurrentIndex(2) + + # remove outliner scriptJobs + for job in self.scriptJobs: + cmds.scriptJob(kill=job, force=True) + + self.populateNetworkList() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def populateNetworkList(self): + + self.nodeNetworkList.clear() + + # get network nodes and add to list widget + modules = cmds.ls(type="network") + returnMods = [] + for module in modules: + attrs = cmds.listAttr(module) + if "parent" in attrs: + returnMods.append(module) + + mainNode = cmds.listConnections(returnMods[0] + ".parent")[0] + self.nodeNetworkList.addItem(mainNode) + + for mod in returnMods: + self.nodeNetworkList.addItem(mod) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def buildUI(self): + # Original Author: Jeremy Ernst + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.setStyleSheet(self.style) + + # size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + scrollSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding) + + # create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWidget.setObjectName("darkImg") + self.setCentralWidget(self.mainWidget) + + # set qt object name + self.setObjectName(windowObject) + self.setWindowTitle(windowTitle) + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) + + # create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + self.setMinimumSize(QtCore.QSize(580, 400)) + self.setMaximumSize(QtCore.QSize(580, 900)) + + self.resize(600, 450) + + # Create a stackedWidget + self.toolModeStack = QtWidgets.QStackedWidget() + self.layout.addWidget(self.toolModeStack) + self.rigMode = QtWidgets.QWidget() + self.mainLayout = QtWidgets.QVBoxLayout(self.rigMode) + self.toolModeStack.addWidget(self.rigMode) + + # create the menu bar + self.menuBar = QtWidgets.QMenuBar() + self.menuBar.setMaximumHeight(25) + self.mainLayout.addWidget(self.menuBar) + + # create the toolbar layout + self.toolFrame = QtWidgets.QFrame() + self.toolFrame.setMinimumHeight(60) + self.toolFrame.setMaximumHeight(60) + self.toolFrame.setObjectName("dark2") + self.mainLayout.addWidget(self.toolFrame) + self.toolbarLayout = QtWidgets.QHBoxLayout(self.toolFrame) + self.toolbarLayout.setDirection(QtWidgets.QBoxLayout.RightToLeft) + self.toolbarLayout.addStretch(0) + self.toolbarLayout.setSpacing(10) + + # toolbar buttons + + self.pinModulesBtn = QtWidgets.QPushButton() + self.pinModulesBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.pinModulesBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/pin.png")) + self.pinModulesBtn.setIconSize(QtCore.QSize(30, 30)) + self.pinModulesBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.pinModulesBtn) + self.pinModulesBtn.setToolTip("Pin a module in place so that moving a parent does not effect the module.") + self.pinModulesBtn.clicked.connect(self.pinModulesUI) + self.pinModulesBtn.setObjectName("toolbar") + + self.bakeOffsetsBtn = QtWidgets.QPushButton() + self.bakeOffsetsBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.bakeOffsetsBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/bakeOffsets.png")) + self.bakeOffsetsBtn.setIconSize(QtCore.QSize(30, 30)) + self.bakeOffsetsBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.bakeOffsetsBtn) + self.bakeOffsetsBtn.setToolTip("Bake the offset mover values up to the global movers.") + self.bakeOffsetsBtn.clicked.connect(self.bakeOffsetsUI) + self.bakeOffsetsBtn.setObjectName("toolbar") + + self.aimModeBtn = QtWidgets.QPushButton() + self.aimModeBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.aimModeBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/aim.png")) + self.aimModeBtn.setIconSize(QtCore.QSize(30, 30)) + self.aimModeBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.aimModeBtn) + self.aimModeBtn.setToolTip("Toggle Aim mode for modules.") + self.aimModeBtn.clicked.connect(self.aimModeUI) + self.aimModeBtn.setObjectName("toolbar") + + self.boneCountBtn = QtWidgets.QPushButton() + self.boneCountBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.boneCountBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/count.png")) + self.boneCountBtn.setIconSize(QtCore.QSize(30, 30)) + self.boneCountBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.boneCountBtn) + self.boneCountBtn.setToolTip("Bring up a bone counter utility") + self.boneCountBtn.setCheckable(True) + self.boneCountBtn.clicked.connect(self.boneCounterUI) + self.boneCountBtn.setObjectName("toolbar") + + self.resetBtn = QtWidgets.QPushButton() + self.resetBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.resetBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/reset.png")) + self.resetBtn.setIconSize(QtCore.QSize(30, 30)) + self.resetBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.resetBtn) + self.resetBtn.setToolTip("Reset Joint Mover Modules Position and Rotation") + self.resetBtn.setCheckable(True) + self.resetBtn.clicked.connect(self.resetModeUI) + self.resetBtn.setObjectName("toolbar") + + self.symmetryModeBtn = QtWidgets.QPushButton() + self.symmetryModeBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.symmetryModeBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/symmetryMode.png")) + self.symmetryModeBtn.setIconSize(QtCore.QSize(30, 30)) + self.symmetryModeBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.symmetryModeBtn) + self.symmetryModeBtn.setToolTip("Brings up a new window to apply symmetry to the selected modules in the list.") + self.symmetryModeBtn.clicked.connect(self.symmetryModeUI) + self.symmetryModeBtn.setObjectName("toolbar") + + self.geoDisplayBtn = QtWidgets.QPushButton() + self.geoDisplayBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.geoDisplayBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/geoDisplay_on.png")) + self.geoDisplayBtn.setIconSize(QtCore.QSize(30, 30)) + self.geoDisplayBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.geoDisplayBtn) + self.geoDisplayBtn.setToolTip("Toggle the visibility of the proxy geometry") + self.geoDisplayBtn.setCheckable(True) + self.geoDisplayBtn.clicked.connect(partial(self.toggleMoverVisibility, "*_proxy_geo", self.geoDisplayBtn)) + self.geoDisplayBtn.clicked.connect( + partial(self.toggleButtonIcon, self.geoDisplayBtn, "System/geoDisplay_on.png", "System/geoDisplay.png")) + self.geoDisplayBtn.setChecked(True) + self.geoDisplayBtn.setObjectName("toolbar") + + self.lraDisplayBtn = QtWidgets.QPushButton() + self.lraDisplayBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.lraDisplayBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/lra_on.png")) + self.lraDisplayBtn.setIconSize(QtCore.QSize(30, 30)) + self.lraDisplayBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.lraDisplayBtn) + self.lraDisplayBtn.setToolTip("Toggle the visibility of the local rotation axis display") + self.lraDisplayBtn.setCheckable(True) + self.lraDisplayBtn.clicked.connect(partial(self.toggleMoverVisibility, "*_lra", self.lraDisplayBtn)) + self.lraDisplayBtn.clicked.connect( + partial(self.toggleButtonIcon, self.lraDisplayBtn, "System/lra_on.png", "System/lra.png")) + self.lraDisplayBtn.setChecked(True) + self.lraDisplayBtn.setObjectName("toolbar") + + self.boneDisplayBtn = QtWidgets.QPushButton() + self.boneDisplayBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.boneDisplayBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/boneDisplay.png")) + self.boneDisplayBtn.setIconSize(QtCore.QSize(30, 30)) + self.boneDisplayBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.boneDisplayBtn) + self.boneDisplayBtn.setToolTip("Toggle the visibility of the bone representations") + self.boneDisplayBtn.setCheckable(True) + self.boneDisplayBtn.clicked.connect(partial(self.toggleMoverVisibility, "*_bone_geo", self.boneDisplayBtn)) + self.boneDisplayBtn.clicked.connect( + partial(self.toggleButtonIcon, self.boneDisplayBtn, "System/boneDisplay_on.png", "System/boneDisplay.png")) + self.boneDisplayBtn.setChecked(False) + self.boneDisplayBtn.setObjectName("toolbar") + + self.meshMoverBtn = QtWidgets.QPushButton() + self.meshMoverBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.meshMoverBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/meshMover.png")) + self.meshMoverBtn.setIconSize(QtCore.QSize(30, 30)) + self.meshMoverBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.meshMoverBtn) + self.meshMoverBtn.setToolTip("Toggle the visibility of the mesh mover controls") + self.meshMoverBtn.setCheckable(True) + self.meshMoverBtn.clicked.connect(partial(self.toggleMoverVisibility, "*_mover_geo", self.meshMoverBtn)) + self.meshMoverBtn.clicked.connect( + partial(self.toggleButtonIcon, self.meshMoverBtn, "System/meshMover_on.png", "System/meshMover.png")) + self.meshMoverBtn.setChecked(False) + self.meshMoverBtn.setObjectName("toolbar") + + self.offsetMoverBtn = QtWidgets.QPushButton() + self.offsetMoverBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.offsetMoverBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/offsetMover.png")) + self.offsetMoverBtn.setIconSize(QtCore.QSize(30, 30)) + self.offsetMoverBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.offsetMoverBtn) + self.offsetMoverBtn.setToolTip("Toggle the visibility of the offset mover controls") + self.offsetMoverBtn.setCheckable(True) + self.offsetMoverBtn.clicked.connect(partial(self.toggleMoverVisibility, "*_mover_offset", self.offsetMoverBtn)) + self.offsetMoverBtn.clicked.connect( + partial(self.toggleButtonIcon, self.offsetMoverBtn, "System/offsetMover_on.png", "System/offsetMover.png")) + self.offsetMoverBtn.setChecked(False) + self.offsetMoverBtn.setObjectName("toolbar") + + self.globalMoverBtn = QtWidgets.QPushButton() + self.globalMoverBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.globalMoverBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/globalMover_on.png")) + self.globalMoverBtn.setIconSize(QtCore.QSize(30, 30)) + self.globalMoverBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.globalMoverBtn) + self.globalMoverBtn.setToolTip("Toggle the visibility of the global mover controls") + self.globalMoverBtn.setCheckable(True) + self.globalMoverBtn.clicked.connect(partial(self.toggleMoverVisibility, "*_mover", self.globalMoverBtn)) + self.globalMoverBtn.clicked.connect( + partial(self.toggleButtonIcon, self.globalMoverBtn, "System/globalMover_on.png", "System/globalMover.png")) + self.globalMoverBtn.setChecked(True) + self.globalMoverBtn.setObjectName("toolbar") + + self.saveTemplateBtn = QtWidgets.QPushButton() + self.saveTemplateBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.saveTemplateBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/save.png")) + self.saveTemplateBtn.setIconSize(QtCore.QSize(30, 30)) + self.saveTemplateBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.saveTemplateBtn) + self.saveTemplateBtn.setToolTip("Save current setup as template") + self.saveTemplateBtn.clicked.connect(self.saveTemplate) + self.saveTemplateBtn.setObjectName("toolbar") + + self.loadTemplateBtn = QtWidgets.QPushButton() + self.loadTemplateBtn.setMinimumSize(QtCore.QSize(30, 30)) + self.loadTemplateBtn.setMaximumSize(QtCore.QSize(30, 30)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/load.png")) + self.loadTemplateBtn.setIconSize(QtCore.QSize(30, 30)) + self.loadTemplateBtn.setIcon(icon) + self.toolbarLayout.addWidget(self.loadTemplateBtn) + self.loadTemplateBtn.setToolTip("Load an existing template file") + self.loadTemplateBtn.clicked.connect(self.loadTemplate) + self.loadTemplateBtn.setObjectName("toolbar") + + # Add Items to the Menu Bar + # icons + icon_saveTemplate = QtGui.QIcon(os.path.join(self.iconsPath, "System/save.png")) + icon_loadTemplate = QtGui.QIcon(os.path.join(self.iconsPath, "System/load.png")) + icon_exit = QtGui.QIcon(os.path.join(self.iconsPath, "System/exit.png")) + icon_viewToolbar = QtGui.QIcon(os.path.join(self.iconsPath, "System/toggleVis.png")) + icon_globalMover = QtGui.QIcon(os.path.join(self.iconsPath, "System/globalMover.png")) + icon_offsetMover = QtGui.QIcon(os.path.join(self.iconsPath, "System/offsetMover.png")) + icon_meshMover = QtGui.QIcon(os.path.join(self.iconsPath, "System/meshMover.png")) + icon_boneDisplay = QtGui.QIcon(os.path.join(self.iconsPath, "System/boneDisplay.png")) + icon_lraDisplay = QtGui.QIcon(os.path.join(self.iconsPath, "System/lra.png")) + icon_proxyGeo = QtGui.QIcon(os.path.join(self.iconsPath, "System/geoDisplay.png")) + icon_massMirrorMode = QtGui.QIcon(os.path.join(self.iconsPath, "System/symmetryMode.png")) + icon_resetModules = QtGui.QIcon(os.path.join(self.iconsPath, "System/reset.png")) + icon_boneCount = QtGui.QIcon(os.path.join(self.iconsPath, "System/count.png")) + + # file + fileMenu = self.menuBar.addMenu("File") + fileMenu.addAction(icon_saveTemplate, "Save Template", self.saveTemplate) + fileMenu.addAction(icon_loadTemplate, "Load Template", self.loadTemplate) + fileMenu.addAction(icon_exit, "Exit") + + # view + viewMenu = self.menuBar.addMenu("View") + viewMenu.addAction(icon_viewToolbar, "Toggle Toolbar Visibility", self.setToolbarVisibility) + viewMenu.addAction(icon_viewToolbar, "View Module Stats", self.moduleStatusUI) + + # display + displayMenu = self.menuBar.addMenu("Display") + displayMenu.addAction(icon_globalMover, "Toggle Global Movers", + partial(self.toggleMoverVisibility_FromMenu, "*_mover", self.globalMoverBtn, + "System/globalMover_on.png", "System/globalMover.png")) + displayMenu.addAction(icon_offsetMover, "Toggle Offset Movers", + partial(self.toggleMoverVisibility_FromMenu, "*_mover_offset", self.offsetMoverBtn, + "System/offsetMover_on.png", "System/offsetMover.png")) + displayMenu.addAction(icon_meshMover, "Toggle Mesh Movers", + partial(self.toggleMoverVisibility_FromMenu, "*_mover_geo", self.meshMoverBtn, + "System/meshMover_on.png", "System/meshMover.png")) + displayMenu.addAction(icon_boneDisplay, "Toggle Joint Display", + partial(self.toggleMoverVisibility_FromMenu, "*_bone_geo", self.boneDisplayBtn, + "System/boneDisplay_on.png", "System/boneDisplay.png")) + displayMenu.addAction(icon_lraDisplay, "Toggle LRA Display", + partial(self.toggleMoverVisibility_FromMenu, "*_lra", self.lraDisplayBtn, + "System/lra_on.png", "System/lra.png")) + displayMenu.addAction(icon_proxyGeo, "Toggle Proxy Geo Display", + partial(self.toggleMoverVisibility_FromMenu, "*_proxy_geo", self.geoDisplayBtn, + "System/geoDisplay_on.png", "System/geoDisplay.png")) + + # tools + toolsMenu = self.menuBar.addMenu("Tools") + toolsMenu.addAction(icon_massMirrorMode, "Mass Mirror Mode Tool", self.symmetryModeUI) + toolsMenu.addAction(icon_resetModules, "Reset Modules Tool", self.resetModeUI) + toolsMenu.addAction(icon_boneCount, "Bone Counter Tool", self.boneCounterUI) + + # help + helpMenu = self.menuBar.addMenu("Help") + helpMenu.addAction("Documentation") + helpMenu.addAction("About") + + # create the tabLayout (Skeleton Creation and Settings/Outliner) + + # tab stylesheet (tab stylesheet via QSS doesn't seem to work for some reason + stylesheet = """ + QTabBar::tab + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(90,90,90), stop:1 rgb(30,30,30)); + border: 2px solid black; + width: 180px; + padding-left: -10px; + font: 8pt; + } + QTabBar::tab:selected + { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(255,174,0), stop:1 rgb(30,30,30)); + border: 2px solid black; + font: bold 10pt; + } + QTabBar::tab:hover + { + background: rgb(132,95,16); + font: bold 10pt; + } + QTabBar::tab:!selected + { + margin-top: 5px; + } + QTabWidget::pane + { + border: 2px solid rgb(0,0,0); + } + """ + + self.tabWidget = QtWidgets.QTabWidget() + self.mainLayout.addWidget(self.tabWidget) + self.tabWidget.setMinimumSize(QtCore.QSize(545, 400)) + self.tabWidget.setMaximumSize(QtCore.QSize(545, 900)) + self.tabWidget.setSizePolicy(scrollSizePolicy) + self.tabWidget.setStyleSheet(stylesheet) + + # Create our first tab + self.tab1 = QtWidgets.QFrame() + self.tab1.setObjectName("dark") + self.topLevelLayout = QtWidgets.QVBoxLayout(self.tab1) + self.tabWidget.addTab(self.tab1, "Creation/Settings") + + # create the label layout + self.labelLayout = QtWidgets.QHBoxLayout() + self.topLevelLayout.addLayout(self.labelLayout) + + # create a label for Modules and Installed Modules + self.modLabel = QtWidgets.QLabel("Rig Modules:") + font = QtGui.QFont() + font.setPointSize(12) + self.modLabel.setFont(font) + self.modLabel.setFrameShape(QtWidgets.QFrame.WinPanel) + self.modLabel.setAlignment(QtCore.Qt.AlignCenter) + self.modLabel.setSizePolicy(mainSizePolicy) + self.modLabel.setMinimumSize(QtCore.QSize(150, 30)) + self.modLabel.setMaximumSize(QtCore.QSize(150, 30)) + self.modLabel.setStyleSheet( + 'color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(25, 175, 255, 255), stop:1 rgba(25, 175, 255, 255));background-color: rgb(60,60,60);') + self.labelLayout.addWidget(self.modLabel) + + # create the installed modules label + self.installedModLabel = QtWidgets.QLabel("Installed Modules:") + font = QtGui.QFont() + font.setPointSize(12) + self.installedModLabel.setFont(font) + self.installedModLabel.setFrameShape(QtWidgets.QFrame.WinPanel) + self.installedModLabel.setAlignment(QtCore.Qt.AlignCenter) + self.installedModLabel.setSizePolicy(mainSizePolicy) + self.installedModLabel.setMinimumSize(QtCore.QSize(360, 30)) + self.installedModLabel.setMaximumSize(QtCore.QSize(360, 30)) + self.installedModLabel.setStyleSheet( + 'color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(25, 175, 255, 255), stop:1 rgba(25, 175, 255, 255));background-color: rgb(60,60,60);') + self.labelLayout.addWidget(self.installedModLabel) + + # create the search layout + self.searchLayout = QtWidgets.QHBoxLayout() + self.topLevelLayout.addLayout(self.searchLayout) + + # create the search bars for both sides + self.moduleSearch = QtWidgets.QLineEdit() + self.moduleSearch.setPlaceholderText("Search...") + self.moduleSearch.setMinimumSize(QtCore.QSize(150, 20)) + self.moduleSearch.setMaximumSize(QtCore.QSize(150, 20)) + self.moduleSearch.setSizePolicy(mainSizePolicy) + self.searchLayout.addWidget(self.moduleSearch) + self.moduleSearch.textChanged.connect(self.searchModules) + + self.installedSearch = QtWidgets.QLineEdit() + self.installedSearch.setPlaceholderText("Search...") + self.installedSearch.setMinimumSize(QtCore.QSize(150, 20)) + self.installedSearch.setMaximumSize(QtCore.QSize(150, 20)) + self.installedSearch.setSizePolicy(mainSizePolicy) + self.searchLayout.addWidget(self.installedSearch) + self.installedSearch.textChanged.connect(self.searchInstalled) + + # add buttons (expand all, collapse all, sort by type, sort by ABC) + self.installed_ExpandAll = QtWidgets.QPushButton() + self.installed_ExpandAll.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + self.installed_ExpandAll.setMinimumSize(QtCore.QSize(40, 20)) + self.installed_ExpandAll.setMaximumSize(QtCore.QSize(40, 20)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/expand_all.png")) + self.installed_ExpandAll.setIcon(icon) + self.installed_ExpandAll.setIconSize(QtCore.QSize(40, 20)) + self.installed_ExpandAll.setToolTip("Expand all module settings") + self.searchLayout.addWidget(self.installed_ExpandAll) + self.installed_ExpandAll.clicked.connect(partial(self.expandAllSettings, True)) + self.installed_ExpandAll.setObjectName("toolbar") + + self.installed_CollapseAll = QtWidgets.QPushButton() + self.installed_CollapseAll.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + self.installed_CollapseAll.setMinimumSize(QtCore.QSize(40, 20)) + self.installed_CollapseAll.setMaximumSize(QtCore.QSize(40, 20)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/collapse_all.png")) + self.installed_CollapseAll.setIcon(icon) + self.installed_CollapseAll.setIconSize(QtCore.QSize(40, 20)) + self.installed_CollapseAll.setToolTip("Collapse all module settings") + self.searchLayout.addWidget(self.installed_CollapseAll) + self.installed_CollapseAll.clicked.connect(partial(self.expandAllSettings, False)) + self.installed_CollapseAll.setObjectName("toolbar") + + self.installed_sortByType = QtWidgets.QPushButton() + self.installed_sortByType.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + self.installed_sortByType.setMinimumSize(QtCore.QSize(40, 20)) + self.installed_sortByType.setMaximumSize(QtCore.QSize(40, 20)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/sortByType.png")) + self.installed_sortByType.setIcon(icon) + self.installed_sortByType.setIconSize(QtCore.QSize(40, 20)) + self.installed_sortByType.setToolTip("Sort modules by module type") + self.searchLayout.addWidget(self.installed_sortByType) + self.installed_sortByType.clicked.connect(partial(self.sortModules, "type")) + self.installed_sortByType.setObjectName("toolbar") + + self.installed_sortByAlphabet = QtWidgets.QPushButton() + self.installed_sortByAlphabet.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + self.installed_sortByAlphabet.setMinimumSize(QtCore.QSize(40, 20)) + self.installed_sortByAlphabet.setMaximumSize(QtCore.QSize(40, 20)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/sortByAlphabet.png")) + self.installed_sortByAlphabet.setIcon(icon) + self.installed_sortByAlphabet.setIconSize(QtCore.QSize(40, 20)) + self.installed_sortByAlphabet.setToolTip("Sort modules by module alphabetical order") + self.searchLayout.addWidget(self.installed_sortByAlphabet) + self.installed_sortByAlphabet.clicked.connect(partial(self.sortModules, "abc")) + self.installed_sortByAlphabet.setObjectName("toolbar") + + # create the layout for the main part of our UI + self.main = QtWidgets.QHBoxLayout() + self.topLevelLayout.addLayout(self.main) + + # create the module scroll area and add it to the main layout + self.scrollAreaMods = QtWidgets.QScrollArea() + self.scrollAreaMods.setObjectName("epic") + self.main.addWidget(self.scrollAreaMods) + self.scrollAreaMods.setSizePolicy(mainSizePolicy) + self.scrollAreaMods.setMinimumSize(QtCore.QSize(150, 400)) + self.scrollAreaMods.setMaximumSize(QtCore.QSize(150, 60000)) + self.scrollAreaMods.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + # Module scroll area contents + self.modScrollAreaContents = QtWidgets.QFrame() + self.modScrollAreaContents.setObjectName("epic") + self.modScrollAreaContents.setSizePolicy(scrollSizePolicy) + self.modScrollAreaContents.setMinimumSize(QtCore.QSize(150, 800)) + self.modScrollAreaContents.setMaximumSize(QtCore.QSize(150, 60000)) + self.scrollAreaMods.setWidget(self.modScrollAreaContents) + + # add the vertical box layout for the module scroll area + self.moduleLayout = QtWidgets.QVBoxLayout(self.modScrollAreaContents) + self.moduleLayout.setDirection(QtWidgets.QBoxLayout.BottomToTop) + self.moduleLayout.setContentsMargins(5, 5, 0, 5) + self.moduleLayout.addStretch(2) + self.moduleLayout.setSpacing(5) + + # create the scroll area for the module settings + self.scrollAreaSettings = QtWidgets.QScrollArea() + self.scrollAreaSettings.setObjectName("epic") + self.scrollAreaSettings.setWidgetResizable(True) + self.main.addWidget(self.scrollAreaSettings) + self.scrollAreaSettings.setSizePolicy(scrollSizePolicy) + self.scrollAreaSettings.setMinimumSize(QtCore.QSize(360, 400)) + self.scrollAreaSettings.setMaximumSize(QtCore.QSize(360, 600000)) + self.scrollAreaSettings.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + # create the module settings scroll area contents widget + self.settingsScrollAreaContents = QtWidgets.QFrame() + self.settingsScrollAreaContents.setObjectName("dark") + self.settingsScrollAreaContents.setMinimumWidth(360) + self.settingsScrollAreaContents.setMaximumWidth(360) + self.settingsScrollAreaContents.setSizePolicy(scrollSizePolicy) + self.scrollAreaSettings.setWidget(self.settingsScrollAreaContents) + + # add the vertical box layout for the module scroll area + self.moduleSettingsLayout = QtWidgets.QVBoxLayout(self.settingsScrollAreaContents) + self.moduleSettingsLayout.setDirection(QtWidgets.QBoxLayout.BottomToTop) + self.moduleSettingsLayout.setContentsMargins(0, 5, 0, 5) + self.moduleSettingsLayout.addStretch(2) + self.moduleSettingsLayout.setSpacing(10) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # SECOND TAB # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + # Create the 2nd tab + self.tab2 = QtWidgets.QFrame(self.tabWidget) + self.tab2.setObjectName("mid") + self.tab2Layout = QtWidgets.QHBoxLayout(self.tab2) + self.tabWidget.addTab(self.tab2, "Outliner") + + # create the treeView representing the outliner + self.treeWidget = QtWidgets.QTreeWidget() + self.treeWidget.setIndentation(10) + self.treeWidget.expandAll() + + self.tab2Layout.addWidget(self.treeWidget) + self.treeWidget.setSizePolicy(mainSizePolicy) + self.treeWidget.setMinimumSize(QtCore.QSize(320, 500)) + self.treeWidget.setMaximumSize(QtCore.QSize(320, 800)) + + # create columns + self.treeWidget.headerItem().setText(0, "Modules") + self.treeWidget.headerItem().setText(1, "G") + self.treeWidget.headerItem().setText(2, "O") + self.treeWidget.headerItem().setText(3, "M") + + # set column widths + self.treeWidget.setColumnWidth(0, 240) + self.treeWidget.setColumnWidth(1, 20) + self.treeWidget.setColumnWidth(2, 20) + self.treeWidget.setColumnWidth(3, 20) + self.treeWidget.setObjectName("light") + self.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # CHANNEL BOX # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # add the channel box layout + self.channelBoxLayout = QtWidgets.QFrame() + self.channelBoxLayout.setObjectName("mid") + + # set dimensions + self.channelBoxLayout.setSizePolicy(scrollSizePolicy) + self.channelBoxLayout.setGeometry(0, 0, 250, 500) + self.channelBoxLayout.setMinimumSize(QtCore.QSize(220, 500)) + self.channelBoxLayout.setMaximumSize(QtCore.QSize(220, 800)) + self.tab2Layout.addWidget(self.channelBoxLayout) + + # create a VBoxLayout for the channelBox Layout + self.channelBoxVLayout = QtWidgets.QVBoxLayout(self.channelBoxLayout) + + # create the QWidget to house the channelBox + self.channelBoxWidget = QtWidgets.QFrame() + self.channelBoxWidget.setObjectName("mid") + + # set dimensions + self.channelBoxWidget.setSizePolicy(mainSizePolicy) + self.channelBoxWidget.setMinimumSize(QtCore.QSize(220, 470)) + self.channelBoxWidget.setMaximumSize(QtCore.QSize(220, 770)) + + # add the channel box widget to the VBoxLayout + self.channelBoxVLayout.addWidget(self.channelBoxWidget) + + # add the channel box VBoxLayout to the QFrame + self.channelBox_mainLayout = QtWidgets.QVBoxLayout(self.channelBoxWidget) + + # add the channel box from Maya to the UI + channelBoxWidget = cmds.channelBox() + pointer = mui.MQtUtil.findControl(channelBoxWidget) + self.channelBox = shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + self.channelBox_mainLayout.addWidget(self.channelBox) + self.channelBox.setObjectName("dark") + self.channelBox.show() + + # add network node info + self.nodeNetworkList = QtWidgets.QListWidget() + self.channelBox_mainLayout.addWidget(self.nodeNetworkList) + self.nodeNetworkList.setMinimumHeight(200) + self.nodeNetworkList.setMaximumHeight(200) + self.nodeNetworkList.itemClicked.connect(self.selectNetworkNode) + + # add the "Finalize Setup" button + self.createSkeletonLayout = QtWidgets.QHBoxLayout() + self.createSkeletonLayout.setContentsMargins(25, 0, 0, 0) + self.createSkeletonBtn = QtWidgets.QPushButton("FINALIZE SETUP") + self.createSkeletonBtn.setObjectName("blueButtonSpecial") + self.createSkeletonBtn.setMinimumHeight(50) + self.createSkeletonBtn.setMaximumHeight(50) + + self.createSkeletonBtn.clicked.connect(self.finalizeSetup_UI) + self.createSkeletonLayout.addWidget(self.createSkeletonBtn) + self.mainLayout.addLayout(self.createSkeletonLayout) + + # build deformation UI + self.buildDeformation_UI() + + # set the layout + self.setLayout(self.mainLayout) + + # find and populate modules list + self.findRigModules() + + # build lock page + self.rigLockedPage = QtWidgets.QFrame() + self.toolModeStack.addWidget(self.rigLockedPage) + + self.rigLockedPage.setMinimumSize(QtCore.QSize(550, 500)) + self.rigLockedPage.setMaximumSize(QtCore.QSize(550, 800)) + image = utils.returnNicePath(self.iconsPath, "System/rigLockPage.png") + self.rigLockedPage.setStyleSheet("background-image: url(" + image + ");") + + # add the layout to the lock page + self.lockLayout = QtWidgets.QVBoxLayout(self.rigLockedPage) + self.lockLayout.setContentsMargins(70, 500, 70, 160) + + buttonLayout1 = QtWidgets.QHBoxLayout() + self.lockLayout.addLayout(buttonLayout1) + + buttonLayout2 = QtWidgets.QHBoxLayout() + self.lockLayout.addLayout(buttonLayout2) + + # add spacer and button + self.unlockBtn = QtWidgets.QPushButton("Remove Rig") + self.unlockBtn.clicked.connect(partial(self.removeRigging)) + buttonLayout1.addWidget(self.unlockBtn) + self.unlockBtn.setMinimumSize(200, 40) + self.unlockBtn.setMaximumSize(200, 40) + self.unlockBtn.setFont(font) + self.unlockBtn.setStyleSheet(self.style) + self.unlockBtn.setObjectName("blueButton") + + self.skinToolsBtn = QtWidgets.QPushButton("Deformation Toolkit") + self.skinToolsBtn.clicked.connect(partial(self.deformationTools)) + buttonLayout2.addWidget(self.skinToolsBtn) + self.skinToolsBtn.setMinimumSize(200, 40) + self.skinToolsBtn.setMaximumSize(200, 40) + self.skinToolsBtn.setStyleSheet(self.style) + self.skinToolsBtn.setObjectName("blueButton") + + self.exportMeshesBtn = QtWidgets.QPushButton("Export Skeletal Meshes") + self.exportMeshesBtn.clicked.connect(partial(self.exportMeshes)) + buttonLayout2.addWidget(self.exportMeshesBtn) + self.exportMeshesBtn.setMinimumSize(200, 40) + self.exportMeshesBtn.setMaximumSize(200, 40) + self.exportMeshesBtn.setStyleSheet(self.style) + self.exportMeshesBtn.setObjectName("blueButton") + + self.rigHistBtn = QtWidgets.QPushButton("Rig History") + self.rigHistBtn.clicked.connect(partial(self.rigHistoryUI)) + buttonLayout1.addWidget(self.rigHistBtn) + self.rigHistBtn.setMinimumSize(200, 40) + self.rigHistBtn.setMaximumSize(200, 40) + self.rigHistBtn.setStyleSheet(self.style) + self.rigHistBtn.setObjectName("blueButton") + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def setToolbarVisibility(self): + # Original Author: Jeremy Ernst + state = self.toolFrame.isVisible() + if state == True: + self.toolFrame.setVisible(False) + if state == False: + self.toolFrame.setVisible(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def setMoverVisibility(self): + # Original Author: Jeremy Ernst + + movers = [["*_proxy_geo", self.geoDisplayBtn], ["*_lra", self.lraDisplayBtn], + ["*_bone_geo", self.boneDisplayBtn], ["*_mover_geo", self.meshMoverBtn], + ["*_mover_offset", self.offsetMoverBtn], ["*_mover", self.globalMoverBtn]] + for mover in movers: + self.toggleMoverVisibility(mover[0], mover[1]) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def sortModules(self, sortMethod): + # Original Author: Jeremy Ernst + + modules = utils.returnRigModules() + modTypes = [] + modNames = [] + # get the module's type and name + for module in modules: + modType = cmds.getAttr(module + ".moduleType") + modName = cmds.getAttr(module + ".moduleName") + modTypes.append([modName, modType]) + modNames.append(str(modName)) + + if sortMethod == "abc": + # create a sorted list of that info + modNames = sorted(modNames, key=str.lower) + + # create a list of the groupbox widgets sorted by alphabetical order + groupBoxes = [] + for i in range(self.moduleSettingsLayout.count()): + try: + groupBoxes.append([self.moduleSettingsLayout.itemAt(i).widget().title(), + self.moduleSettingsLayout.itemAt(i).widget()]) + except: + pass + + # re-sort the groupboxes in the layout + for each in modNames: + for box in groupBoxes: + if box[0] == each: + self.moduleSettingsLayout.insertWidget(1, box[1]) + self.moduleSettingsLayout.setDirection(QtWidgets.QBoxLayout.BottomToTop) + + if sortMethod == "type": + # create a sorted list of that info + sortedList = sorted(modTypes, key=lambda name: name[1]) + + # create a list of the groupbox widgets sorted by module type + groupBoxes = [] + for i in range(self.moduleSettingsLayout.count()): + try: + groupBoxes.append([self.moduleSettingsLayout.itemAt(i).widget().title(), + self.moduleSettingsLayout.itemAt(i).widget()]) + except: + pass + # compare the sorted list to the list of groupboxes and make a new list that has those groupboxes in the same order as the sorted list + newList = [] + for item in sortedList: + name = item[0] + for box in groupBoxes: + title = box[0] + if title == name: + newList.append(box) + + # re-sort the groupboxes in the layout + for i in range(len(newList)): + self.moduleSettingsLayout.insertWidget(1, newList[i][1]) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def expandAllSettings(self, doExpand): + # Original Author: Jeremy Ernst + + modules = utils.returnRigModules() + + for module in modules: + modType = cmds.getAttr(module + ".moduleType") + modName = cmds.getAttr(module + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + + # get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + # find the instance of that module and call on the skeletonSettings_UI function + moduleInst = moduleClass(self, modName) + + if modType != "ART_Root": + for i in range(self.moduleSettingsLayout.count()): + if type(self.moduleSettingsLayout.itemAt(i).widget()) == QtWidgets.QGroupBox: + if self.moduleSettingsLayout.itemAt(i).widget().title() == modName: + self.moduleSettingsLayout.itemAt(i).widget().setChecked(doExpand) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def addModule(self, baseName, className): + # Original Author: Jeremy Ernst + + # This function gets called when a module button is pushed. It will grab some information from the user and then add the network node, jointMover, and skelSettingsUI + + # delete the UI if it already exists + mayaWindow = interfaceUtils.getMainWindow() + mayaWindow = mayaWindow.objectName() + if cmds.window(mayaWindow + "|pyArtAddModuleUi", q=True, exists=True): + cmds.deleteUI(mayaWindow + "|pyArtAddModuleUi") + + # run the user interface to gather information + import ART_AddModuleUI as ART_AddModuleUI + reload(ART_AddModuleUI) + inst = ART_AddModuleUI.ART_AddModule_UI(baseName, className, self, interfaceUtils.getMainWindow()) + inst.show() + + return True + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleButtonIcon(self, button, onIcon, offIcon): + # Original Author: Jeremy Ernst + + state = button.isChecked() + + if state: + icon = QtGui.QIcon(os.path.join(self.iconsPath, onIcon)) + else: + icon = QtGui.QIcon(os.path.join(self.iconsPath, offIcon)) + + button.setIcon(icon) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleMoverVisibility_FromMenu(self, searchKey, button, onIcon, offIcon): + # Original Author: Jeremy Ernst + + state = button.isChecked() + if state: + button.setChecked(False) + icon = QtGui.QIcon(os.path.join(self.iconsPath, offIcon)) + button.setIcon(icon) + + else: + button.setChecked(True) + icon = QtGui.QIcon(os.path.join(self.iconsPath, onIcon)) + button.setIcon(icon) + + self.toggleMoverVisibility(searchKey, button) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleMoverVisibility(self, searchKey, button): + # Original Author: Jeremy Ernst + + try: + currentSelection = cmds.ls(sl=True) + # get current checkbox state + state = button.isChecked() + + # get the list of movers + cmds.select(searchKey) + movers = cmds.ls(sl=True) + + # find the mover shapes and set their visibility + shapes = [] + for mover in movers: + child = cmds.listRelatives(mover, children=True, shapes=True) + if len(child) > 0: + shapes.append(mover + "|" + child[0]) + + for shape in shapes: + cmds.setAttr(shape + ".v", lock=False) + cmds.setAttr(shape + ".v", state, lock=True) + + try: + cmds.select(currentSelection) + except: + pass + + except: + pass + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def saveTemplate(self): + # Original Author: Jeremy Ernst + + # find all modules in the scene + if cmds.objExists("ART_RIG_ROOT"): + modules = cmds.listConnections("ART_RIG_ROOT.rigModules") + + # start creating the data list + data = [] + + # loop through each module, getting the required information + for module in modules: + moduleData = [str(module)] + + # get the module attributes and their values + attrData = [] + attrs = cmds.listAttr(module, ud=True, hd=True) + for attr in attrs: + value = cmds.getAttr(module + "." + attr) + attrData.append([str(attr), value]) + + # add the attrData to the moduleData + moduleData.append(attrData) + + # get all movers and thier keyable values + moduleName = cmds.getAttr(module + ".moduleName") + moverTypes = ["_mover", "_mover_offset", "_mover_geo"] + allMoverData = [] + + for moverType in moverTypes: + try: + cmds.select(moduleName + "*" + moverType) + movers = cmds.ls(sl=True) + validMovers = [] + + # validate selection + for mover in movers: + moverGrp = moduleName + "_mover_grp" + validMovers.append(moverGrp) + children = cmds.listRelatives(moverGrp, ad=True) + if mover in children: + validMovers.append(str(mover)) + + # get mover values + for mover in validMovers: + moverData = [] + attrs = cmds.listAttr(mover, keyable=True) + + for attr in attrs: + value = cmds.getAttr(mover + "." + attr) + moverData.append([str(mover + "." + attr), value]) + allMoverData.append(moverData) + + except: + pass + + # add all of the mover data to the moduleData list + moduleData.append(allMoverData) + + # add the moduleData to data + data.append(moduleData) + + # ask for the file name to give the template + startingDir = os.path.normcase(os.path.join(self.toolsPath, "Core/JointMover/Templates")) + if not os.path.exists(startingDir): + os.makedirs(startingDir) + + filename = cmds.fileDialog2(fm=0, okc="Save Template", dir=startingDir, ff="*.template")[0] + + # create the template file + f = open(filename, 'w') + + # dump the data with json + json.dump(data, f) + f.close() + + # print out confirmation + print "Template has been saved" + # inViewMessage only available in 2014 and up + try: + cmds.inViewMessage(amg=' <hl>Template has been saved.</hl>', pos='topCenter', fade=True) + except: + print "inViewMessage not supported" + pass + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def loadTemplate(self): + # Original Author: Jeremy Ernst + + # make sure scene is new, and refresh UI + if self.moduleSettingsLayout.count() > 2: + self.unsavedChanges() + return + + # prompt for the file to load + startingDir = os.path.normcase(os.path.join(self.toolsPath, "Core/JointMover/Templates")) + if not os.path.exists: + startingDir = self.toolsPath + try: + filename = cmds.fileDialog2(fm=1, okc="Load Template", dir=startingDir, ff="*.template")[0] + except: + return + + if filename is not None: + + self.moduleInstances = [] + + # load the data + json_file = open(filename) + data = json.load(json_file) + json_file.close() + + # clear out the scene of any network nodes, joint movers and relaunch the UI + modules = cmds.listConnections("ART_RIG_ROOT.rigModules") + modules.append("ART_RIG_ROOT") + cmds.delete(modules) + + if cmds.objExists("JointMover"): + cmds.delete("JointMover") + + for material in ["blue_m", "green_m", "red_m", "white_m"]: + if cmds.objExists(material): + cmds.delete(material) + + # relaunch the UI + newUI = createUI() + + # create a progress window + progWindow = cmds.progressWindow(title='Loading Template', progress=0) + amount = (100 / len(data)) / 5 + + # go through the template data, adding the modules, the settings UI, and the joint mover + solveLast = [] + for each in data: + currentAmount = cmds.progressWindow(query=True, progress=True) + cmds.progressWindow(edit=True, progress=currentAmount + amount, status="Working on " + each[0]) + + module = each[0] + if module is not None: + moduleAttrData = each[1] + for attr in moduleAttrData: + if attr[0] == "moduleName": + moduleName = attr[1] + if attr[0] == "moduleType": + moduleClass = str(attr[1]) + moduleType = str(attr[1]) + + # build the network node for the module + if module != "ART_Root_Module": + mod = __import__("RigModules." + moduleClass, {}, {}, [moduleClass]) + moduleClass = getattr(mod, mod.className) + jmPath = mod.jointMover + moduleInst = moduleClass(newUI, moduleName) + networkNode = moduleInst.buildNetwork() + + # set the attributes on the network node + for moduleAttr in moduleAttrData: + attr = moduleAttr[0] + value = moduleAttr[1] + + try: + attrType = str(cmds.getAttr(networkNode + "." + attr, type=True)) + cmds.setAttr(networkNode + "." + attr, lock=False) + if attrType == "string": + cmds.setAttr(networkNode + "." + attr, value, type=attrType, lock=True) + else: + cmds.setAttr(networkNode + "." + attr, value, lock=True) + + except: + print attr, value, attrType + + # arm/leg exceptions + specialCaseModules = ["ART_Leg_Standard", "ART_Arm_Standard"] + if moduleType in specialCaseModules: + side = cmds.getAttr(networkNode + ".side") + jmPath = jmPath.partition(".ma")[0] + "_" + side + ".ma" + + # torso exception + if moduleType == "ART_Torso": + numSpine = int(cmds.getAttr(networkNode + ".spineJoints")) + jmPath = jmPath.partition(".ma")[0].rpartition("_")[0] + "_" + str(numSpine) + "Spine.ma" + + # head exception + if moduleType == "ART_Head": + numNeck = int(cmds.getAttr(networkNode + ".neckJoints")) + jmPath = jmPath.partition(".ma")[0].rpartition("_")[0] + "_" + str(numNeck) + "Neck.ma" + + # build settings UI/build JM/Add to Outliner + currentAmount = cmds.progressWindow(query=True, progress=True) + cmds.progressWindow(edit=True, progress=currentAmount + amount) + + moduleInst.skeletonSettings_UI(moduleName) + moduleInst.jointMover_Build(jmPath) + moduleInst.addJointMoverToOutliner() + moduleInst.applyModuleChanges(moduleInst) + + # copy, reset, and paste settings to update the UI/scene one last time + # this was initially put in to get leaf module proxy shape/control shape to actually load in + # properly without this, the settings would be correct, but the scene would not update, since + # the combo boxes had not been hooked up yet + skipModules = ["ART_Torso", "ART_Head"] + if moduleType not in skipModules: + moduleInst.copySettings() + moduleInst.resetSettings() + moduleInst.pasteSettings() + self.moduleInstances.append(moduleInst) + + # position the movers according to the template + currentAmount = cmds.progressWindow(query=True, progress=True) + cmds.progressWindow(edit=True, progress=currentAmount + amount) + + # Apply the positional data + movers = each[2] + for mover in movers: + for each in mover: + + moduleAttr = each[0] + attrValue = each[1] + + try: + cmds.setAttr(moduleAttr, attrValue) + except: + pass + + # bake offsets + moduleInst.bakeOffsets() + + # hook up mover to parent + currentAmount = cmds.progressWindow(query=True, progress=True) + cmds.progressWindow(edit=True, progress=currentAmount + amount) + + parent = "" + for attr in moduleAttrData: + if attr[0] == "parentModuleBone": + parent = attr[1] + + mover = "" + if parent == "root": + mover = "root_mover" + offsetMover = "root_mover" + + else: + # find the parent mover name to parent to + networkNodes = utils.returnRigModules() + mover = utils.findMoverNodeFromJointName(networkNodes, parent, False, True) + offsetMover = utils.findMoverNodeFromJointName(networkNodes, parent) + + if mover is not None: + cmds.parentConstraint(mover, moduleName + "_mover_grp", mo = True) + cmds.scaleConstraint(mover, moduleName + "_mover_grp", mo = True) + + # create the connection geo between the two + currentAmount = cmds.progressWindow(query=True, progress=True) + cmds.progressWindow(edit=True, progress=currentAmount + amount) + + childMover = utils.findOffsetMoverFromName(moduleName) + riggingUtils.createBoneConnection(offsetMover, childMover, moduleName) + + globalMover = utils.findGlobalMoverFromName(moduleName) + cmds.select(globalMover) + cmds.setToolTo("moveSuperContext") + + utils.fitViewAndShade() + + else: + solveLast.append(each) + + # solve root mover last + for each in solveLast: + # position the movers according to the template + currentAmount = cmds.progressWindow(query=True, progress=True) + cmds.progressWindow(edit=True, progress=currentAmount + amount) + + movers = each[2] + for mover in movers: + for eachMover in mover: + + moduleAttr = eachMover[0] + attrValue = eachMover[1] + + try: + cmds.setAttr(moduleAttr, attrValue) + except: + pass + + cmds.progressWindow(endProgress=1) + createUI() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findRigModules(self): + # Original Author: Jeremy Ernst + + # get rig module files + modulesLocation = os.path.normcase(os.path.join(self.toolsPath, "Core/Scripts/RigModules")) + files = os.listdir(modulesLocation) + modules = [] + + for f in files: + if f.rpartition(".")[2] == "py": + modules.append(f) + + for mod in modules: + niceName = mod.rpartition(".")[0] + if niceName != "__init__" and niceName != "ART_Root": + # create the push button for the module and set the size + button = QtWidgets.QPushButton() + button.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + button.setMinimumSize(QtCore.QSize(125, 75)) + button.setMaximumSize(QtCore.QSize(125, 75)) + + # get the icon path from the module and add the icon to the push button + module = __import__("RigModules." + niceName, {}, {}, [niceName]) + reload(module) + icon = module.icon + hoverIcon = module.hoverIcon + + searchTerm = module.search + className = module.className + baseName = module.baseName + iconFile = utils.returnFriendlyPath(os.path.join(self.iconsPath, icon)) + hoverIcon = utils.returnFriendlyPath(os.path.join(self.iconsPath, hoverIcon)) + + # create stylesheet + style = """ + + QPushButton + { + background-image: url(iconfilepath); + } + + + QPushButton:hover + { + background-image: url(hoverfilepath); + margin-top: -2px; + } + + """ + + buttonStyle = style.replace("iconfilepath", iconFile) + buttonStyle = buttonStyle.replace("hoverfilepath", hoverIcon) + + # set properties for filtering later + button.setObjectName(searchTerm) + button.setProperty("name", searchTerm) + button.setStyleSheet(buttonStyle) + self.moduleLayout.addWidget(button) + + # setup signal + button.clicked.connect(partial(self.addModule, baseName, className)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def searchModules(self): + # Original Author: Jeremy Ernst + + searchText = self.moduleSearch.text() + + for i in range(self.moduleLayout.count()): + if type(self.moduleLayout.itemAt(i).widget()) == QtWidgets.QPushButton: + self.moduleLayout.itemAt(i).widget().setVisible(False) + moduleType = self.moduleLayout.itemAt(i).widget().property("name") + searchKeys = moduleType.split(":") + + for key in searchKeys: + if key.find(searchText) != -1: + self.moduleLayout.itemAt(i).widget().setVisible(True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def searchInstalled(self): + # Original Author: Jeremy Ernst + + searchText = self.installedSearch.text() + + for i in range(self.moduleSettingsLayout.count()): + if type(self.moduleSettingsLayout.itemAt(i).widget()) == QtWidgets.QGroupBox: + self.moduleSettingsLayout.itemAt(i).widget().setVisible(False) + title = self.moduleSettingsLayout.itemAt(i).widget().title() + + if title.find(searchText) != -1: + self.moduleSettingsLayout.itemAt(i).widget().setVisible(True) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def editSetup(self): + # Original Author: Jeremy Ernst + + # set model pose if exists + for inst in self.moduleInstances: + networkNode = inst.returnNetworkNode + if cmds.objExists(networkNode + ".modelPose"): + inst.setReferencePose("modelPose") + + # remove weight table script job + cmds.scriptJob(kill=self.skinToolsInst.wtScriptJob) + + # show index 0 of stacked widget + self.toolModeStack.setCurrentIndex(0) + + # change state in network node + cmds.setAttr("ART_RIG_ROOT.state", 0) + + # find meshes that are weighted + weightedMeshes = [] + skinClusters = cmds.ls(type='skinCluster') + + for cluster in skinClusters: + geometry = cmds.skinCluster(cluster, q=True, g=True)[0] + geoTransform = cmds.listRelatives(geometry, parent=True)[0] + if geoTransform.find("proxy_geo") == -1: + weightedMeshes.append([geoTransform, cluster]) + + # save out weights of meshes + for mesh in weightedMeshes: + filePath = utils.returnFriendlyPath(os.path.join(cmds.internalVar(utd=True), mesh[0] + ".WEIGHTS")) + print "saving out skin weights for " + mesh[0] + " at: " + filePath + + # export skin weights + skin = riggingUtils.export_skin_weights(filePath, mesh[0]) + + # delete history of meshes + cmds.delete(mesh[0], ch=True) + + # delete skeleton + cmds.delete("root") + + # delete proxy geo grp if it exists + if cmds.objExists("skinned_proxy_geo"): + cmds.delete("skinned_proxy_geo") + + # unhide/lock joint mover + cmds.select("JointMover", hi=True) + jmNodes = cmds.ls(sl=True) + for node in jmNodes: + cmds.lockNode(node, lock=False) + + lockNodes = cmds.listRelatives("JointMover", children=True) + for node in lockNodes: + cmds.setAttr(node + ".v", lock=False) + cmds.setAttr(node + ".v", 1) + + # clear selection + cmds.select(clear=True) + + # recreate outliner scriptjobs + self.scriptJobs = [] + for module in self.moduleInstances: + module.createScriptJob() + + + ############################################################################################################## + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # EXTERNAL FILE CALLS + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ############################################################################################################## + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def buildDeformation_UI(self): + # Original Author: Jeremy Ernst + + import ART_SkinTools + reload(ART_SkinTools) + self.skinToolsInst = ART_SkinTools.ART_SkinTools(self, True, self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def deformationTools(self): + # Original Author: Jeremy Ernst + + import ART_SkinTools + reload(ART_SkinTools) + self.skinToolsInst = ART_SkinTools.ART_SkinTools(self, False, interfaceUtils.getMainWindow()) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def aimModeUI(self): + # Original Author: Jeremy Ernst + + import ART_AimModeUI + reload(ART_AimModeUI) + ART_AimModeUI.ART_AimMode(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def pinModulesUI(self): + # Original Author: Jeremy Ernst + import ART_PinModules + reload(ART_PinModules) + ART_PinModules.ART_PinModules(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def bakeOffsetsUI(self): + # Original Author: Jeremy Ernst + + import ART_BakeOffsetsUI + reload(ART_BakeOffsetsUI) + ART_BakeOffsetsUI.ART_BakeOffsets(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def boneCounterUI(self): + # Original Author: Jeremy Ernst + + import ART_BoneCounter + reload(ART_BoneCounter) + inst = ART_BoneCounter.ART_BoneCounter(self) + self.boneCounterInst = inst + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def resetModeUI(self): + + import ART_ResetModeUI + reload(ART_ResetModeUI) + ART_ResetModeUI.ART_ResetMode(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def symmetryModeUI(self): + # Original Author: Jeremy Ernst + + import ART_SymmetryModeUI + reload(ART_SymmetryModeUI) + ART_SymmetryModeUI.ART_SymmetryMode(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def finalizeSetup_UI(self): + # Original Author: Jeremy Ernst + + import ART_FinalizeSetup + reload(ART_FinalizeSetup) + ART_FinalizeSetup.ART_FinalizeSetup(self, self.skinToolsInst) + # need to also pass in deformation ui instance + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildRig(self): + # Original Author: Jeremy Ernst + + # run publish process + import ART_Publish + reload(ART_Publish) + ART_Publish.ART_Publish(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def removeRigging(self): + # Original Author: Jeremy Ernst + + # reset scale if needed + if cmds.objExists("master_anim.globalScale"): + cmds.setAttr("master_anim.globalScale", 1) + cmds.refresh() + + # disconnect main deformation hiearchy + cmds.select("root", hi=True) + joints = cmds.ls(sl=True, type="joint") + + for joint in joints: + attrs = ["translate", "rotate", "scale"] + for attr in attrs: + try: + cmds.disconnectAttr("driver_" + joint + "." + attr, joint + "." + attr) + except Exception, e: + print str(e) + + # unlock nodes + cmds.select("rig_grp", hi=True) + rigNodes = cmds.ls(sl=True) + for node in rigNodes: + cmds.lockNode(node, lock=False) + + # go through each rig and delete that module's rigging + for module in self.moduleInstances: + module.deleteRig() + + # set the state of the character + self.toolModeStack.setCurrentIndex(1) + if cmds.objExists("ART_RIG_ROOT.state"): + cmds.setAttr("ART_RIG_ROOT.state", 1) + + # remove outliner scriptJobs + for job in self.scriptJobs: + cmds.scriptJob(kill=job, force=True) + + # build the scriptJob for the weight table + self.scriptJobs.append(self.skinToolsInst.weightTable_scriptJob()) + + # delete driver skeleton + if cmds.objExists("driver_root"): + cmds.delete("driver_root") + + # set model pose + for inst in self.moduleInstances: + networkNode = inst.returnNetworkNode + if cmds.objExists(networkNode + ".modelPose"): + inst.setSkeletonPose("modelPose") + + # remove the skeletal constraints + for inst in self.moduleInstances: + networkNode = inst.returnNetworkNode + if cmds.objExists(networkNode + ".modelPose"): + inst.removeSkeletalConstraints() + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def rigHistoryUI(self): + + import ART_RigHistoryUI as arh + reload(arh) + arh.ART_RigHistoryUI(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def moduleStatusUI(self): + + import ART_ModuleStatus as ART_ModuleStatus + reload(ART_ModuleStatus) + ART_ModuleStatus.run(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def unsavedChanges(self): + # Original Author: Jeremy Ernst + + # message box for letting user know current file has unsaved changes + msgBox = QtWidgets.QMessageBox() + msgBox.setIcon(QtWidgets.QMessageBox.Warning) + msgBox.setText("Current File Has Unsaved Changes!") + msgBox.setDetailedText("To load a template, please create a new file and re-launch the tool.") + ret = msgBox.exec_() + + return ret + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def exportMeshes(self): + # Original Author: Jeremy Ernst + + # run publish process + import ART_ExportMeshes + reload(ART_ExportMeshes) + inst = ART_ExportMeshes.ART_ExportMeshes(self, parent=interfaceUtils.getMainWindow()) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def selectNetworkNode(self): + + selection = self.nodeNetworkList.currentItem() + cmds.select(selection.text()) + + +############################################################################################################## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# NON-CLASS FUNCTIONS +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +############################################################################################################## + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +def createUI(): + # Original Author: Jeremy Ernst + + global parent + global gui + + try: + + gui.close() + gui.deleteLater() + + except: + pass + + # if the rigCreatorUI exists delete UI + if cmds.dockControl("pyArtRigCreatorDock", q=True, exists=True): + cmds.deleteUI(windowObject) + cmds.deleteUI("pyArtRigCreatorDock", control=True) + + # create an instance of the UI and add it to a Maya dock + gui = ART_RigCreator_UI(interfaceUtils.getMainWindow()) + allowedAreas = ["left", "right"] + dockControl = cmds.dockControl("pyArtRigCreatorDock", area="right", content=windowObject, allowedArea=allowedAreas, + label=windowTitle, w=450, h=500) + cmds.refresh(force=True) + cmds.dockControl("pyArtRigCreatorDock", e=True, r=True) + return gui diff --git a/Core/Scripts/Interfaces/ART_RigHistoryUI.py b/Core/Scripts/Interfaces/ART_RigHistoryUI.py new file mode 100644 index 0000000..20540e6 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_RigHistoryUI.py @@ -0,0 +1,136 @@ +''' +Created on Aug 26, 2015 + +@author: jeremy.ernst +''' + +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +import maya.cmds as cmds +import os, json +import System.utils as utils + + + + +class ART_RigHistoryUI(): + #Original Author: Jeremy Ernst + + def __init__(self, mainUI): + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + + self.mainUI = mainUI + self.rigData = [] + self.warnings = 0 + self.errors = 0 + + #images + self.imageBkgrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/toolbar_background.png")) + self.imageBtnBkrd = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/blue_field_background.png")) + self.frameBackground = utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/field_background.png")) + + + #build the UI + if cmds.window("ART_RigHistWin", exists = True): + cmds.deleteUI("ART_RigHistWin", wnd = True) + + self.buildUI() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildUI(self): + #Original Author: Jeremy Ernst + + #create the main window + self.mainWin = QtWidgets.QMainWindow(self.mainUI) + self.mainWin.setStyleSheet("background-color: rgb(0, 0, 0);, color: rgb(0,0,0);") + + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.mainWin.setStyleSheet(self.style) + + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.mainWin.setCentralWidget(self.mainWidget) + + #set qt object name + self.mainWin.setObjectName("ART_RigHistWin") + self.mainWin.setWindowTitle("Rig History") + + #font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + #set size policy + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + + self.mainWin.resize(400, 240) + self.mainWin.setSizePolicy(mainSizePolicy) + self.mainWin.setMinimumSize(QtCore.QSize( 400, 240 )) + self.mainWin.setMaximumSize(QtCore.QSize( 400, 240 )) + + #create the QFrame for this page + self.background = QtWidgets.QFrame() + self.layout.addWidget(self.background) + self.mainLayout = QtWidgets.QVBoxLayout(self.background) + self.background.setObjectName("epic") + + + #detailed information + self.infoText = QtWidgets.QTextEdit() + self.mainLayout.addWidget(self.infoText) + self.infoText.setMinimumSize(QtCore.QSize( 360, 200 )) + self.infoText.setMaximumSize(QtCore.QSize( 360, 200 )) + self.infoText.setReadOnly(True) + self.infoText.setWordWrapMode(QtGui.QTextOption.WordWrap) + + #show the window + self.mainWin.show() + + self.getHistory() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def getHistory(self): + + + characterNode = "ART_RIG_ROOT" + data = json.loads(cmds.getAttr(characterNode + ".versionNote")) + + for each in data: + + version = each[0] + info = each[1] + user = each[2] + + self.infoText.setTextColor(QtGui.QColor(236,217,0)) + self.infoText.append("Version #: " + str(version)) + self.infoText.setTextColor(QtGui.QColor(255,255,255)) + self.infoText.append("Description: " + info) + self.infoText.setTextColor(QtGui.QColor(0,255,0)) + self.infoText.append("User: " + user + "\n\n") + + diff --git a/Core/Scripts/Interfaces/ART_SelectControlsUI.py b/Core/Scripts/Interfaces/ART_SelectControlsUI.py new file mode 100644 index 0000000..9e75139 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_SelectControlsUI.py @@ -0,0 +1,357 @@ + +import json +import os +from functools import partial + +import maya.cmds as cmds + +import System.interfaceUtils as interfaceUtils +import System.utils as utils +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + + +class ART_SelectControls(object): + def __init__(self, animPickerUI, showUI, parent=None): + + super(ART_SelectControls, self).__init__() + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.scriptPath = settings.value("scriptPath") + self.projectPath = settings.value("projectPath") + + self.pickerUI = animPickerUI + self.showUI = showUI + + # write out qss based on user settings + stylesheetDir = utils.returnNicePath(self.scriptPath, "Interfaces/StyleSheets/") + stylesheets = os.listdir(stylesheetDir) + + for sheet in stylesheets: + interfaceUtils.writeQSS(os.path.join(stylesheetDir, sheet)) + + # build the UI or just go straight to selecting all controls + if self.showUI: + self.buildUI() + else: + self.selectControls() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def buildUI(self): + + if cmds.window("pyART_SelectControlsWIN", exists=True): + cmds.deleteUI("pyART_SelectControlsWIN", wnd=True) + + # create the main window + self.mainWin = QtWidgets.QMainWindow(self.pickerUI) + + # create the main widget + self.mainWidget = QtWidgets.QFrame() + self.mainWidget.setObjectName("dark") + self.mainWin.setCentralWidget(self.mainWidget) + + # create the mainLayout + self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/animPicker.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + self.mainWin.setStyleSheet(self.style) + + self.mainWin.setMinimumSize(QtCore.QSize(400, 250)) + self.mainWin.setMaximumSize(QtCore.QSize(400, 250)) + self.mainWin.resize(400, 250) + + # set qt object name + self.mainWin.setObjectName("pyART_SelectControlsWIN") + self.mainWin.setWindowTitle("Select Rig Controls") + + self.layout = QtWidgets.QHBoxLayout() + self.mainLayout.addLayout(self.layout) + + # LEFT SIDE + # list of modules + self.moduleList = QtWidgets.QListWidget() + self.moduleList.setMinimumSize(QtCore.QSize(180, 230)) + self.moduleList.setMaximumSize(QtCore.QSize(180, 230)) + self.layout.addWidget(self.moduleList) + self.moduleList.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) + + self.moduleList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.moduleList.customContextMenuRequested.connect(self.createContextMenu) + + # RIGHT SIDE + self.rightLayout = QtWidgets.QVBoxLayout() + self.layout.addLayout(self.rightLayout) + + # character combo + self.characterCombo = QtWidgets.QComboBox() + self.rightLayout.addWidget(self.characterCombo) + self.characterCombo.setMinimumSize(QtCore.QSize(180, 60)) + self.characterCombo.setMaximumSize(QtCore.QSize(180, 60)) + self.characterCombo.setIconSize(QtCore.QSize(50, 50)) + self.characterCombo.currentIndexChanged.connect(partial(self.findCharacterModules)) + + # select options + self.fkControlsCB = QtWidgets.QCheckBox("FK Controls") + self.fkControlsCB.setChecked(True) + self.fkControlsCB.setToolTip( + "If this is checked, for the selected modules,\nall FK controls will be selected or added to the selection.") + self.rightLayout.addWidget(self.fkControlsCB) + + self.ikControlsCB = QtWidgets.QCheckBox("IK Controls") + self.ikControlsCB.setChecked(True) + self.ikControlsCB.setToolTip( + "If this is checked, for the selected modules,\nall IK controls will be selected or added to the selection.") + self.rightLayout.addWidget(self.ikControlsCB) + + self.specialControlsCB = QtWidgets.QCheckBox("Special Controls") + self.specialControlsCB.setChecked(True) + self.specialControlsCB.setToolTip( + "If this is checked, for the selected modules,\nany control that is not FK or IK (like dynamics for example),\nwill be selected or added to the selection.") + self.rightLayout.addWidget(self.specialControlsCB) + + self.selectSettingsCB = QtWidgets.QCheckBox("Include Settings") + self.selectSettingsCB.setChecked(True) + self.selectSettingsCB.setToolTip( + "If this is checked, for the selected modules,\nall settings nodes will be included in the selection.") + self.rightLayout.addWidget(self.selectSettingsCB) + + self.selectSpacesCB = QtWidgets.QCheckBox("Include Spaces") + self.selectSpacesCB.setChecked(True) + self.selectSpacesCB.setToolTip( + "If this is checked, for the selected modules,\nall space switching nodes will be included in the selection.") + self.rightLayout.addWidget(self.selectSpacesCB) + + self.rightLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + self.selectCtrlsBtn = QtWidgets.QPushButton("Select Controls") + self.selectCtrlsBtn.setMinimumWidth(180) + self.selectCtrlsBtn.setMaximumWidth(180) + self.rightLayout.addWidget(self.selectCtrlsBtn) + self.selectCtrlsBtn.setObjectName("blueButton") + self.selectCtrlsBtn.clicked.connect(self.selectControls) + + # show window + if self.showUI: + self.mainWin.show() + + # populate UI + self.findCharacters() + self.findCharacterModules() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def createContextMenu(self, point): + + self.contextMenu = QtWidgets.QMenu() + + selectIcon = QtGui.QIcon((utils.returnFriendlyPath(os.path.join(self.iconsPath, "System/select.png")))) + + self.contextMenu.addAction(selectIcon, "Select All", self.selectAllInList) + self.contextMenu.addAction("Clear Selection", self.clearListSelection) + + self.contextMenu.exec_(self.moduleList.mapToGlobal(point)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def selectAllInList(self): + + self.moduleList.selectAll() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def clearListSelection(self): + + self.moduleList.clearSelection() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCharacters(self): + + allNodes = cmds.ls(type="network") + characterNodes = [] + for node in allNodes: + attrs = cmds.listAttr(node) + if "rigModules" in attrs: + characterNodes.append(node) + + # go through each node, find the character name, the namespace on the node, and the picker attribute + for node in characterNodes: + try: + namespace = cmds.getAttr(node + ".namespace") + except: + namespace = cmds.getAttr(node + ".name") + + # add the icon found on the node's icon path attribute to the tab + iconPath = cmds.getAttr(node + ".iconPath") + iconPath = utils.returnNicePath(self.projectPath, iconPath) + icon = QtGui.QIcon(iconPath) + + self.characterCombo.addItem(icon, namespace) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def findCharacterModules(self, *args): + + if self.showUI: + self.moduleList.clear() + + # current character + selectedChar = self.characterCombo.currentText() + + # get rig modules + if cmds.objExists(selectedChar + ":" + "ART_RIG_ROOT"): + modules = cmds.listConnections(selectedChar + ":" + "ART_RIG_ROOT.rigModules") + + for module in modules: + modName = cmds.getAttr(module + ".moduleName") + item = QtWidgets.QListWidgetItem(modName) + item.setData(QtCore.Qt.UserRole, module) + self.moduleList.addItem(item) + + else: + index = self.pickerUI.characterTabs.currentIndex() + selectedChar = self.pickerUI.characterTabs.tabToolTip(index) + + if cmds.objExists(selectedChar + ":" + "ART_RIG_ROOT"): + modules = cmds.listConnections(selectedChar + ":" + "ART_RIG_ROOT.rigModules") + + return modules + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def selectControls(self, selectFK=True, selectIK=True, selectSpecial=True, includeSettings=True, + includeSpaces=True): + + # get selection settings + if self.showUI: + selectFK = self.fkControlsCB.isChecked() + selectIK = self.ikControlsCB.isChecked() + selectSpecial = self.specialControlsCB.isChecked() + includeSettings = self.selectSettingsCB.isChecked() + includeSpaces = self.selectSpacesCB.isChecked() + + selected = self.moduleList.selectedItems() + selectedChar = self.characterCombo.currentText() + + else: + selected = self.findCharacterModules() + index = self.pickerUI.characterTabs.currentIndex() + selectedChar = self.pickerUI.characterTabs.tabToolTip(index) + + # create list to store controls to select later + controlsToSelect = [] + + # go through each selected module, or all modules if not showing UI + for each in selected: + if self.showUI: + module = each.data(QtCore.Qt.UserRole) + else: + module = each + + # get inst + modType = cmds.getAttr(module + ".moduleType") + modName = cmds.getAttr(module + ".moduleName") + mod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(mod) + + # get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(mod, mod.className) + + # find the instance of that module + moduleInst = moduleClass(self, modName) + + # set namespace for instance + moduleInst.namespace = selectedChar + ":" + + # get controls for module + networkNode = moduleInst.returnRigNetworkNode + + if len(moduleInst.controlTypes) == 0: + cmds.warning(str(modName) + " does not have controlTypes list implemented in __init__!") + + # SELECTION OPTIONS + if selectFK: + for each in mod.controlTypes: + print each + if each[1] == "FK": + if cmds.objExists(networkNode + "." + each[0]): + data = json.loads(cmds.getAttr(networkNode + "." + each[0])) + if data is not None: + if len(data) > 0: + for item in data: + controlsToSelect.append(selectedChar + ":" + item) + + if selectIK: + for each in mod.controlTypes: + if each[1] == "IK": + if cmds.objExists(networkNode + "." + each[0]): + data = json.loads(cmds.getAttr(networkNode + "." + each[0])) + if data is not None: + if len(data) > 0: + for item in data: + controlsToSelect.append(selectedChar + ":" + item) + + if selectSpecial: + for each in mod.controlTypes: + if each[1] == "Special": + if cmds.objExists(networkNode + "." + each[0]): + data = json.loads(cmds.getAttr(networkNode + "." + each[0])) + if data is not None: + if len(data) > 0: + for item in data: + controlsToSelect.append(selectedChar + ":" + item) + + if includeSettings: + moduleName = cmds.getAttr(networkNode + ".moduleName") + controlsToSelect.append(selectedChar + ":" + moduleName + "_settings") + + if includeSpaces: + cmds.warning("Include Spaces is not implemented yet") + + # select all the things + cmds.select(clear=True) + for each in controlsToSelect: + if cmds.objExists(each): + cmds.select(each, add=True) + + print str(len(controlsToSelect)) + " objects selected" diff --git a/Core/Scripts/Interfaces/ART_SetMirrorModuleUI.py b/Core/Scripts/Interfaces/ART_SetMirrorModuleUI.py new file mode 100644 index 0000000..ab995b8 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_SetMirrorModuleUI.py @@ -0,0 +1,188 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +import maya.cmds as cmds +import System.utils as utils +import System.interfaceUtils as interfaceUtils + +#maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + + + + +def getMainWindow(): + import maya.OpenMayaUI as mui + pointer = mui.MQtUtil.mainWindow() + #pyside QMainWindow takes in a QWidget rather than QObject + return shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + + + + +windowTitle = "Set Mirror Module" +windowObject = "pyArtSetMirrorModuleUi" + + + +class ART_SetMirrorModule_UI(QtWidgets.QMainWindow): + + def __init__(self, moduleInst, rigUiInst, parent = None): + + + super(ART_SetMirrorModule_UI, self).__init__(parent) + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + #create class variables + self.modInst = moduleInst + self.rigUiInst = rigUiInst + + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + style = f.read() + f.close() + + self.setStyleSheet(style) + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + #set qt object name + self.setObjectName(windowObject) + self.setWindowTitle(windowTitle) + + #create the mainLayout for the rig creator UI + self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget) + self.mainLayout.setContentsMargins(0, 0, 0, 0) + + self.setSizePolicy(mainSizePolicy) + self.setMinimumSize(QtCore.QSize( 250, 200 )) + self.setMaximumSize(QtCore.QSize( 250, 200 )) + + #create the background image + self.frame = QtWidgets.QFrame() + self.mainLayout.addWidget(self.frame) + + #create the layout for the widgets + self.widgetLayout = QtWidgets.QVBoxLayout(self.frame) + + + label = QtWidgets.QLabel("Choose Mirror Module:") + self.widgetLayout.addWidget(label) + font = QtGui.QFont() + font.setBold(True) + + self.moduleList = QtWidgets.QListWidget() + self.moduleList.addItem("None") + self.widgetLayout.addWidget(self.moduleList) + + + #add items to comboBox + networkNode = self.modInst.returnNetworkNode + modules = utils.returnRigModules() + for mod in modules: + modName = cmds.getAttr(mod + ".moduleName") + modType = cmds.getAttr(mod + ".moduleType") + + if modType == cmds.getAttr(networkNode + ".moduleType"): + if mod != networkNode: + self.moduleList.addItem(modName) + + self.moduleList.setCurrentRow(0) + + + + spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.widgetLayout.addItem(spacerItem1) + + + #update button + self.updateBtn = QtWidgets.QPushButton("UPDATE") + self.widgetLayout.addWidget(self.updateBtn) + self.updateBtn.setMinimumSize(QtCore.QSize(230, 40)) + self.updateBtn.setMaximumSize(QtCore.QSize(230, 40)) + self.updateBtn.setSizePolicy(mainSizePolicy) + font = QtGui.QFont() + font.setPointSize(12) + self.updateBtn.setFont(font) + self.updateBtn.setObjectName("blueButton") + + #hookup signal/slot on create button + self.updateBtn.clicked.connect(self.setMirrorModule) + + + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def setMirrorModule(self): + + #get new parent + mirrorModule = self.moduleList.currentItem().text() + + + #update current parent text + self.modInst.mirrorMod.setText(mirrorModule) + + #update network node parentModuleBone attribute + networkNode = self.modInst.returnNetworkNode + cmds.setAttr(networkNode + ".mirrorModule", lock = False) + cmds.setAttr(networkNode + ".mirrorModule", mirrorModule, type = "string", lock = True) + + + #also do this change to the mirror as well + modules = utils.returnRigModules() + for mod in modules: + modName = cmds.getAttr(mod + ".moduleName") + if modName == mirrorModule: + + #set the mirrored version + mirror = cmds.getAttr(networkNode + ".moduleName") + + cmds.setAttr(mod + ".mirrorModule", lock = False) + cmds.setAttr(mod + ".mirrorModule", mirror, type = "string", lock = True) + + + #get instance of mirror module's class + modType = cmds.getAttr(mod + ".moduleType") + modName = cmds.getAttr(mod + ".moduleName") + module = __import__("RigModules." + modType, {}, {}, [modType]) + reload(module) + + #get the class name from that module file (returns Modules.ART_Root.ART_Root for example) + moduleClass = getattr(module, module.className) + + #find the instance of that module and call on the skeletonSettings_UI function + moduleInst = moduleClass(self.rigUiInst, modName) + + #update mirrorModtext + #find the current groupBox for this module + for i in range(self.rigUiInst.moduleSettingsLayout.count()): + if type(self.rigUiInst.moduleSettingsLayout.itemAt(i).widget()) == QtWidgets.QGroupBox: + if self.rigUiInst.moduleSettingsLayout.itemAt(i).widget().title() == modName: + self.rigUiInst.moduleSettingsLayout.itemAt(i).widget().setParent(None) + + #relaunch the skeleton settings UI with new info + moduleInst.skeletonSettings_UI(modName) + + + + #delete the UI + mayaWindow = interfaceUtils.getMainWindow() + mayaWindow = mayaWindow.objectName() + cmds.deleteUI(mayaWindow + "|" + windowObject)
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/ART_SkinTools.py b/Core/Scripts/Interfaces/ART_SkinTools.py new file mode 100644 index 0000000..782f5bf --- /dev/null +++ b/Core/Scripts/Interfaces/ART_SkinTools.py @@ -0,0 +1,2878 @@ +import math +import os +from functools import partial + +import maya.OpenMayaUI as mui +import maya.cmds as cmds +import maya.mel as mel + +from ThirdParty.Qt import QtGui, QtCore, QtWidgets + +# maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + +import System.utils as utils +import System.riggingUtils as riggingUtils + + +def getMainWindow(): + import maya.OpenMayaUI as mui + pointer = mui.MQtUtil.mainWindow() + # pyside QMainWindow takes in a QWidget rather than QObject + return shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + + +class ART_SkinTools(QtWidgets.QMainWindow): + def __init__(self, mainUI, attachToRigInterface, parent): + + super(ART_SkinTools, self).__init__(parent=None) + + # get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + self.mainUI = mainUI + + # build the UI + self.buildSkinToolsUI(attachToRigInterface) + + # UI Skin Cluster + self.skinCluster = None + + # how to run the UI + if attachToRigInterface: + self.mainUI.toolModeStack.addWidget(self.deformationTabWidget) + + # standalone (WORK IN PROGRESS) + else: + self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + self.setMinimumSize(QtCore.QSize(580, 600)) + self.setMaximumSize(QtCore.QSize(580, 900)) + self.setContentsMargins(0, 0, 0, 0) + self.resize(580, 900) + self.setCentralWidget(self.deformationTabWidget) + + self.show() + self.weightTable_scriptJob() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def buildSkinToolsUI(self, attachToRigInterface): + + # font + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + # load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + + styleSheetFile = utils.returnNicePath(self.toolsPath, + "Core/Scripts/Interfaces/StyleSheets/toolbarButtonsScheme.qss") + f = open(styleSheetFile, "r") + self.toolStyle = f.read() + f.close() + + self.setStyleSheet(self.style) + + # add the widget to the stacked widget + self.deformationTabWidget = QtWidgets.QWidget() + self.deformation_mainLayout = QtWidgets.QVBoxLayout(self.deformationTabWidget) + + # create the menu bar + if attachToRigInterface: + self.deformation_menuBar = QtWidgets.QMenuBar() + self.deformation_menuBar.setMaximumHeight(20) + self.deformation_mainLayout.addWidget(self.deformation_menuBar) + + # add items to menu bar + debugMenu = self.deformation_menuBar.addMenu("Development Tools") + debugMenu.addAction("Test Building Rigs", self.debugRigs) + + # create the toolbar layout + self.deformation_toolFrame = QtWidgets.QFrame() + self.deformation_toolFrame.setStyleSheet(self.toolStyle) + self.deformation_toolFrame.setObjectName("dark") + self.deformation_toolFrame.setMaximumHeight(52) + + self.deformation_mainLayout.addWidget(self.deformation_toolFrame) + + self.deformation_toolbarLayout = QtWidgets.QHBoxLayout(self.deformation_toolFrame) + self.deformation_toolbarLayout.setDirection(QtWidgets.QBoxLayout.LeftToRight) + self.deformation_toolbarLayout.setSpacing(10) + + # toolbar buttons + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/paintWeights.png") + self.weightTable_paintWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_paintWeightsBtn) + self.weightTable_paintWeightsBtn.setMinimumSize(35, 35) + self.weightTable_paintWeightsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_paintWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_paintWeightsBtn.setIcon(icon) + self.weightTable_paintWeightsBtn.clicked.connect(self.weightTable_paintWeightsMode) + self.weightTable_paintWeightsBtn.setToolTip("Paint Skin Weights Mode") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/addRemove.png") + self.weightTable_addRemoveInfsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_addRemoveInfsBtn) + self.weightTable_addRemoveInfsBtn.setMinimumSize(35, 35) + self.weightTable_addRemoveInfsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_addRemoveInfsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_addRemoveInfsBtn.setIcon(icon) + self.weightTable_addRemoveInfsBtn.clicked.connect(self.addOrRemoveInfs_UI) + self.weightTable_addRemoveInfsBtn.setToolTip("Add or Remove Influences") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/mirrorWeights.png") + self.weightTable_mirrorWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_mirrorWeightsBtn) + self.weightTable_mirrorWeightsBtn.setMinimumSize(35, 35) + self.weightTable_mirrorWeightsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_mirrorWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_mirrorWeightsBtn.setIcon(icon) + self.weightTable_mirrorWeightsBtn.clicked.connect(self.paintSkinWeights_mirrorSkinWeights) + self.weightTable_mirrorWeightsBtn.setToolTip("Mirror Skin Weights") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/importSkin.png") + self.weightTable_importSkinWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_importSkinWeightsBtn) + self.weightTable_importSkinWeightsBtn.setMinimumSize(35, 35) + self.weightTable_importSkinWeightsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_importSkinWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_importSkinWeightsBtn.setIcon(icon) + self.weightTable_importSkinWeightsBtn.setFont(headerFont) + self.weightTable_importSkinWeightsBtn.clicked.connect(self.paintSkinWeights_importSkinWeights) + self.weightTable_importSkinWeightsBtn.setToolTip("Import Skin Weights") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/exportSkin.png") + self.weightTable_exportSkinWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_exportSkinWeightsBtn) + self.weightTable_exportSkinWeightsBtn.setMinimumSize(35, 35) + self.weightTable_exportSkinWeightsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_exportSkinWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_exportSkinWeightsBtn.setIcon(icon) + self.weightTable_exportSkinWeightsBtn.clicked.connect(self.paintSkinWeights_exportSkinWeights) + self.weightTable_exportSkinWeightsBtn.setToolTip("Export Skin Weights") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/smartCopy.png") + self.weightTable_smartCopyWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_smartCopyWeightsBtn) + self.weightTable_smartCopyWeightsBtn.setMinimumSize(35, 35) + self.weightTable_smartCopyWeightsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_smartCopyWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_smartCopyWeightsBtn.setIcon(icon) + self.weightTable_smartCopyWeightsBtn.clicked.connect(self.paintSkinWeights_copySkinWeights) + self.weightTable_smartCopyWeightsBtn.setToolTip("Copy Skin Weights") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/moveInfs.png") + self.weightTable_moveInfsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_moveInfsBtn) + self.weightTable_moveInfsBtn.setMinimumSize(35, 35) + self.weightTable_moveInfsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_moveInfsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_moveInfsBtn.setIcon(icon) + self.weightTable_moveInfsBtn.clicked.connect(partial(self.moveInfluences_UI)) + self.weightTable_moveInfsBtn.setToolTip("Move Influence Tool") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/fixWeights.png") + self.weightTable_fixWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_fixWeightsBtn) + self.weightTable_fixWeightsBtn.setMinimumSize(35, 35) + self.weightTable_fixWeightsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_fixWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_fixWeightsBtn.setIcon(icon) + self.weightTable_fixWeightsBtn.setFont(headerFont) + self.weightTable_fixWeightsBtn.setToolTip("Fix Skin Weights") + self.weightTable_fixWeightsBtn.clicked.connect(riggingUtils.fixSkinWeights) + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/hammer.png") + self.weightTable_hammerWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_hammerWeightsBtn) + self.weightTable_hammerWeightsBtn.setMinimumSize(35, 35) + self.weightTable_hammerWeightsBtn.setMaximumSize(35, 35) + self.weightTable_hammerWeightsBtn.setToolTip("Hammer Weights Tool") + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_hammerWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_hammerWeightsBtn.setIcon(icon) + self.weightTable_hammerWeightsBtn.clicked.connect(self.paintSkinWeights_hammerSkinWeights) + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/wizard.png") + self.weightTable_weightWizardBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_weightWizardBtn) + self.weightTable_weightWizardBtn.setMinimumSize(35, 35) + self.weightTable_weightWizardBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_weightWizardBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_weightWizardBtn.setIcon(icon) + self.weightTable_weightWizardBtn.clicked.connect(self.paintSkinWeights_deformationWizardLaunch) + self.weightTable_weightWizardBtn.setToolTip("Launch Skinning Wizard") + + buttonBkrd = utils.returnNicePath(self.iconsPath, "System/bindTool.png") + self.weightTable_bindWeightsBtn = QtWidgets.QPushButton() + self.deformation_toolbarLayout.addWidget(self.weightTable_bindWeightsBtn) + self.weightTable_bindWeightsBtn.setMinimumSize(35, 35) + self.weightTable_bindWeightsBtn.setMaximumSize(35, 35) + icon = QtGui.QIcon(buttonBkrd) + self.weightTable_bindWeightsBtn.setIconSize(QtCore.QSize(30, 30)) + self.weightTable_bindWeightsBtn.setIcon(icon) + self.weightTable_bindWeightsBtn.clicked.connect(self.paintSkinWeights_smoothSkinUI) + self.weightTable_bindWeightsBtn.setToolTip("Smooth Bind Tool") + + self.deformation_toolbarLayout.addSpacerItem( + QtWidgets.QSpacerItem(500, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)) + + # Add Items to the Menu Bar + + # Add the main widget that will house all of the main UI components + self.defomation_mainWidget = QtWidgets.QStackedWidget() + self.defomation_mainWidget.setObjectName("dark") + self.defomation_mainWidget.setMinimumSize(QtCore.QSize(560, 400)) + self.defomation_mainWidget.setMaximumSize(QtCore.QSize(560, 900)) + self.deformation_mainLayout.addWidget(self.defomation_mainWidget) + + # add pages to the stackedwidget + self.defomation_subWidget = QtWidgets.QWidget() + self.defomation_mainWidget.addWidget(self.defomation_subWidget) + self.deformationPaintWeightsWidget = QtWidgets.QWidget() + self.defomation_mainWidget.addWidget(self.deformationPaintWeightsWidget) + + # add hbox layout that splits tools and channel box + self.deformation_hBoxLayout = QtWidgets.QHBoxLayout(self.defomation_subWidget) + self.defomation_mainWidget.setCurrentIndex(0) + + # tools layout scroll layout + self.weightTableScrollArea = QtWidgets.QScrollArea() + self.weightTableScrollArea.setMinimumSize(QtCore.QSize(310, 500)) + self.weightTableScrollArea.setMaximumSize(QtCore.QSize(310, 6000)) + self.weightTableScrollArea.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + self.weightTableScrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.deformation_hBoxLayout.addWidget(self.weightTableScrollArea) + + # frame + self.deformation_toolsLayout = QtWidgets.QFrame() + self.deformation_toolsLayout.setObjectName("dark") + self.deformation_toolsLayout.setMinimumSize(QtCore.QSize(290, 850)) + self.deformation_toolsLayout.setMaximumSize(QtCore.QSize(290, 6000)) + self.weightTableScrollArea.setWidget(self.deformation_toolsLayout) + + self.weightTableMainLayout = QtWidgets.QVBoxLayout(self.deformation_toolsLayout) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # weight table + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + # weight table selection + self.weightTableSelectionFrame = QtWidgets.QGroupBox("Selection") + self.weightTableSelectionFrame.setFont(headerFont) + self.weightTableSelectionFrame.setObjectName("light") + self.weightTableSelectionFrame.setMinimumSize(QtCore.QSize(275, 100)) + self.weightTableSelectionFrame.setMaximumSize(QtCore.QSize(275, 100)) + self.weightTableSelectionMain = QtWidgets.QVBoxLayout(self.weightTableSelectionFrame) + self.weightTableSelection = QtWidgets.QHBoxLayout() + self.weightTableSelectionMain.addLayout(self.weightTableSelection) + self.weightTableMainLayout.addWidget(self.weightTableSelectionFrame) + + self.growSelection = QtWidgets.QPushButton("Grow") + self.weightTableSelection.addWidget(self.growSelection) + self.growSelection.clicked.connect(partial(self.weightTable_growOrShrink, 1)) + self.growSelection.setToolTip("Grow Selection") + self.growSelection.setObjectName("blueButton") + + self.shrinkSelection = QtWidgets.QPushButton("Shrink") + self.weightTableSelection.addWidget(self.shrinkSelection) + self.shrinkSelection.clicked.connect(partial(self.weightTable_growOrShrink, 2)) + self.shrinkSelection.setToolTip("Shrink Selection") + self.shrinkSelection.setObjectName("blueButton") + + self.selectLoop = QtWidgets.QPushButton("Loop") + self.weightTableSelection.addWidget(self.selectLoop) + self.selectLoop.clicked.connect(partial(self.weightTable_loopOrRing, "loop")) + self.selectLoop.setToolTip("Select Edge Loop from Selection") + self.selectLoop.setObjectName("blueButton") + + self.selectRing = QtWidgets.QPushButton("Ring") + self.weightTableSelection.addWidget(self.selectRing) + self.selectRing.clicked.connect(partial(self.weightTable_loopOrRing, "ring")) + self.selectRing.setToolTip("Select Edge Ring from Selection") + self.selectRing.setObjectName("blueButton") + + self.selectShell = QtWidgets.QPushButton("Shell") + self.weightTableSelection.addWidget(self.selectShell) + self.selectShell.clicked.connect(self.weightTable_shell) + self.selectShell.setToolTip("Select Element") + self.selectShell.setObjectName("blueButton") + + self.weightTableIsoLayout = QtWidgets.QHBoxLayout() + self.weightTableSelectionMain.addLayout(self.weightTableIsoLayout) + + self.isolateSelection = QtWidgets.QPushButton("Isolate Selection") + self.weightTableIsoLayout.addWidget(self.isolateSelection) + self.isolateSelection.setCheckable(True) + self.isolateSelection.clicked.connect(self.weightTable_isolate) + self.isolateSelection.setToolTip("Isolate Selection") + self.isolateSelection.setObjectName("blueButton") + + # preset weight values + self.weightTablePresetsFrame = QtWidgets.QGroupBox("Preset Weight Values") + self.weightTablePresetsFrame.setFont(headerFont) + self.weightTablePresetsFrame.setObjectName("light") + self.weightTablePresetsFrame.setMinimumSize(QtCore.QSize(275, 60)) + self.weightTablePresetsFrame.setMaximumSize(QtCore.QSize(275, 60)) + self.weightTablePresets = QtWidgets.QHBoxLayout(self.weightTablePresetsFrame) + self.weightTableMainLayout.addWidget(self.weightTablePresetsFrame) + + self.weightPreset1 = QtWidgets.QPushButton("0") + self.weightTablePresets.addWidget(self.weightPreset1) + self.weightPreset1.clicked.connect(partial(self.weightTable_addWeight, 0.0)) + self.weightPreset1.setObjectName("greenButton") + + self.weightPreset2 = QtWidgets.QPushButton(".1") + self.weightTablePresets.addWidget(self.weightPreset2) + self.weightPreset2.clicked.connect(partial(self.weightTable_addWeight, 0.1)) + self.weightPreset2.setObjectName("greenButton") + + self.weightPreset3 = QtWidgets.QPushButton(".25") + self.weightTablePresets.addWidget(self.weightPreset3) + self.weightPreset3.clicked.connect(partial(self.weightTable_addWeight, 0.25)) + self.weightPreset3.setObjectName("greenButton") + + self.weightPreset4 = QtWidgets.QPushButton(".5") + self.weightTablePresets.addWidget(self.weightPreset4) + self.weightPreset4.clicked.connect(partial(self.weightTable_addWeight, 0.5)) + self.weightPreset4.setObjectName("greenButton") + + self.weightPreset5 = QtWidgets.QPushButton(".75") + self.weightTablePresets.addWidget(self.weightPreset5) + self.weightPreset5.clicked.connect(partial(self.weightTable_addWeight, 0.75)) + self.weightPreset5.setObjectName("greenButton") + + self.weightPreset6 = QtWidgets.QPushButton("1.0") + self.weightTablePresets.addWidget(self.weightPreset6) + self.weightPreset6.clicked.connect(partial(self.weightTable_addWeight, 1.0)) + self.weightPreset6.setObjectName("greenButton") + + # custom weight values + self.weightTableCustomFrame = QtWidgets.QGroupBox("Custom Weight Values") + self.weightTableCustomFrame.setFont(headerFont) + self.weightTableCustomFrame.setObjectName("light") + self.weightTableCustomFrame.setMinimumSize(QtCore.QSize(275, 80)) + self.weightTableCustomFrame.setMaximumSize(QtCore.QSize(275, 80)) + self.weightTableCustom = QtWidgets.QVBoxLayout(self.weightTableCustomFrame) + self.weightTableMainLayout.addWidget(self.weightTableCustomFrame) + + self.setCustomWeightLayout = QtWidgets.QHBoxLayout() + self.weightTableCustom.addLayout(self.setCustomWeightLayout) + + self.scaleCustomWeightLayout = QtWidgets.QHBoxLayout() + self.weightTableCustom.addLayout(self.scaleCustomWeightLayout) + + # set weight + self.setCustomWeight = QtWidgets.QPushButton("Set Weight") + self.setCustomWeightLayout.addWidget(self.setCustomWeight) + self.setCustomWeight.clicked.connect(partial(self.weightTable_addWeight, None)) + self.customWeightField = QtWidgets.QDoubleSpinBox() + self.setCustomWeightLayout.addWidget(self.customWeightField) + self.customWeightField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.setCustomWeight.setObjectName("blueButton") + self.setCustomWeight.setMinimumHeight(25) + + self.customWeightField.setRange(0.0, 1.0) + self.customWeightField.setValue(0.1) + self.customWeightField.setSingleStep(.01) + + self.setCustomWeightIncrease = QtWidgets.QPushButton("+") + self.setCustomWeightLayout.addWidget(self.setCustomWeightIncrease) + self.setCustomWeightIncrease.setMinimumWidth(30) + self.setCustomWeightIncrease.setMaximumWidth(30) + self.setCustomWeightIncrease.clicked.connect(partial(self.weightTable_inrementWeight, "up")) + self.setCustomWeightIncrease.setObjectName("blueButton") + self.setCustomWeightIncrease.setMinimumHeight(25) + + self.setCustomWeightDecrease = QtWidgets.QPushButton("-") + self.setCustomWeightLayout.addWidget(self.setCustomWeightDecrease) + self.setCustomWeightDecrease.setMinimumWidth(30) + self.setCustomWeightDecrease.setMaximumWidth(30) + self.setCustomWeightDecrease.clicked.connect(partial(self.weightTable_inrementWeight, "down")) + self.setCustomWeightDecrease.setObjectName("blueButton") + self.setCustomWeightDecrease.setMinimumHeight(25) + + # scale weight + self.scaleCustomWeight = QtWidgets.QPushButton("Scale Weight") + self.scaleCustomWeightLayout.addWidget(self.scaleCustomWeight) + self.scaleCustomWeight.clicked.connect(partial(self.weightTable_scaleWeight, None)) + self.scaleCustomWeight.setObjectName("blueButton") + self.scaleCustomWeight.setMinimumHeight(25) + + self.scaleCustomWeightField = QtWidgets.QDoubleSpinBox() + self.scaleCustomWeightLayout.addWidget(self.scaleCustomWeightField) + self.scaleCustomWeightField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + + self.scaleCustomWeightField.setRange(0.0, 1.0) + self.scaleCustomWeightField.setValue(0.9) + self.scaleCustomWeightField.setSingleStep(.01) + + self.scaleCustomWeightIncrease = QtWidgets.QPushButton("+") + self.scaleCustomWeightLayout.addWidget(self.scaleCustomWeightIncrease) + self.scaleCustomWeightIncrease.setMinimumWidth(30) + self.scaleCustomWeightIncrease.setMaximumWidth(30) + self.scaleCustomWeightIncrease.clicked.connect(partial(self.weightTable_scaleWeight, "up")) + self.scaleCustomWeightIncrease.setObjectName("blueButton") + self.scaleCustomWeightIncrease.setMinimumHeight(25) + + self.scaleCustomWeightDecrease = QtWidgets.QPushButton("-") + self.scaleCustomWeightLayout.addWidget(self.scaleCustomWeightDecrease) + self.scaleCustomWeightDecrease.setMinimumWidth(30) + self.scaleCustomWeightDecrease.setMaximumWidth(30) + self.scaleCustomWeightDecrease.clicked.connect(partial(self.weightTable_scaleWeight, "down")) + self.scaleCustomWeightDecrease.setObjectName("blueButton") + self.scaleCustomWeightDecrease.setMinimumHeight(25) + + # Value Transfer + self.weightTablevalueXferFrame = QtWidgets.QGroupBox("Value Transfer") + self.weightTablevalueXferFrame.setFont(headerFont) + self.weightTablevalueXferFrame.setObjectName("light") + self.weightTablevalueXferFrame.setMinimumSize(QtCore.QSize(275, 60)) + self.weightTablevalueXferFrame.setMaximumSize(QtCore.QSize(275, 60)) + self.weightTableXferLayout = QtWidgets.QHBoxLayout(self.weightTablevalueXferFrame) + self.weightTableMainLayout.addWidget(self.weightTablevalueXferFrame) + + self.weightTable_copyValues = QtWidgets.QPushButton("Copy") + self.weightTableXferLayout.addWidget(self.weightTable_copyValues) + self.weightTable_copyValues.clicked.connect(self.weightTable_copyWeight) + self.weightTable_copyValues.setObjectName("blueButton") + + self.weightTable_pasteValues = QtWidgets.QPushButton("Paste") + self.weightTableXferLayout.addWidget(self.weightTable_pasteValues) + self.weightTable_pasteValues.clicked.connect(self.weightTable_pasteWeight) + self.weightTable_pasteValues.setObjectName("blueButton") + + self.weightTable_blendValues = QtWidgets.QPushButton("Blend") + self.weightTableXferLayout.addWidget(self.weightTable_blendValues) + self.weightTable_blendValues.clicked.connect(self.weightTable_blendWeight) + self.weightTable_blendValues.setObjectName("blueButton") + + # vertex buffer + self.vertexBufferLayout = QtWidgets.QLineEdit() + self.vertexBufferLayout.setReadOnly(True) + self.weightTableMainLayout.addWidget(self.vertexBufferLayout) + self.vertexBufferLayout.setPlaceholderText("No vertex data copied...") + + # List of skin joints + self.weightTablevalueskinJointsFrame = QtWidgets.QGroupBox("Skin Joints") + self.weightTablevalueskinJointsFrame.setFont(headerFont) + self.weightTablevalueskinJointsFrame.setObjectName("light") + self.weightTablevalueskinJointsFrame.setMinimumSize(QtCore.QSize(275, 200)) + self.weightTablevalueskinJointsFrame.setMaximumSize(QtCore.QSize(275, 200)) + self.weightTablevalueskinJointsLayout = QtWidgets.QHBoxLayout(self.weightTablevalueskinJointsFrame) + self.weightTableMainLayout.addWidget(self.weightTablevalueskinJointsFrame) + + self.weightTable_skinJoints = QtWidgets.QListWidget() + self.weightTablevalueskinJointsLayout.addWidget(self.weightTable_skinJoints) + self.weightTable_skinJoints.itemClicked.connect(partial(self.weightTable_connectInfluenceLists, True, False)) + + # button layout + self.weightTableSkinJointsButtons = QtWidgets.QVBoxLayout() + self.weightTablevalueskinJointsLayout.addLayout(self.weightTableSkinJointsButtons) + + # add show influenced verts button + self.weightTableShowInfVerts = QtWidgets.QPushButton() + self.weightTableSkinJointsButtons.addWidget(self.weightTableShowInfVerts) + self.weightTableShowInfVerts.setMinimumSize(QtCore.QSize(32, 32)) + self.weightTableShowInfVerts.setMaximumSize(QtCore.QSize(32, 32)) + self.weightTableShowInfVerts.setToolTip("Show vertices influenced by selected joint in list.") + self.weightTableShowInfVerts.clicked.connect(partial(self.weightTable_showInfluencedVerts, False)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/showVerts.png")) + self.weightTableShowInfVerts.setIconSize(QtCore.QSize(30, 30)) + self.weightTableShowInfVerts.setIcon(icon) + + # show influenced verts in selection button + self.weightTableShowInfVertsSel = QtWidgets.QPushButton() + self.weightTableSkinJointsButtons.addWidget(self.weightTableShowInfVertsSel) + self.weightTableShowInfVertsSel.setMinimumSize(QtCore.QSize(32, 32)) + self.weightTableShowInfVertsSel.setMaximumSize(QtCore.QSize(32, 32)) + self.weightTableShowInfVertsSel.setToolTip("Show vertices influenced by selected joint in current selection.") + self.weightTableShowInfVertsSel.clicked.connect(partial(self.weightTable_showInfluencedVerts, True)) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/showVertsInSel.png")) + self.weightTableShowInfVertsSel.setIconSize(QtCore.QSize(30, 30)) + self.weightTableShowInfVertsSel.setIcon(icon) + + # toggle joint selection filter + self.weightTableJointSel = QtWidgets.QPushButton() + self.weightTableSkinJointsButtons.addWidget(self.weightTableJointSel) + self.weightTableJointSel.setMinimumSize(QtCore.QSize(32, 32)) + self.weightTableJointSel.setMaximumSize(QtCore.QSize(32, 32)) + self.weightTableJointSel.setToolTip("Toggle Joint Selection Filter.") + self.weightTableJointSel.setCheckable(True) + self.weightTableJointSel.clicked.connect(self.weightTable_toggleJointSelectMode) + icon = QtGui.QIcon(os.path.join(self.iconsPath, "System/jointFilter.png")) + self.weightTableJointSel.setIconSize(QtCore.QSize(30, 30)) + self.weightTableJointSel.setIcon(icon) + + # list of vertices' joints/influence amounts + self.weightTablevalueVertsFrame = QtWidgets.QGroupBox("Vertex Skin Info") + self.weightTablevalueVertsFrame.setObjectName("light") + self.weightTablevalueVertsFrame.setFont(headerFont) + self.weightTablevalueVertsFrame.setMinimumSize(QtCore.QSize(275, 200)) + self.weightTablevalueVertsFrame.setMaximumSize(QtCore.QSize(275, 200)) + self.weightTablevalueVertsLayout = QtWidgets.QHBoxLayout(self.weightTablevalueVertsFrame) + self.weightTableMainLayout.addWidget(self.weightTablevalueVertsFrame) + self.weightTableSplitter = QtWidgets.QSplitter() + self.weightTablevalueVertsLayout.addWidget(self.weightTableSplitter) + + # vertex list + self.weightTableVertList = QtWidgets.QListWidget() + self.weightTableSplitter.addWidget(self.weightTableVertList) + self.weightTableVertList.itemClicked.connect(partial(self.weightTable_connectInfluenceLists, False, True)) + + # set context menu policy on groupbox + self.weightTableVertList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.weightTableVertList.customContextMenuRequested.connect(self.createContextMenuWeightTable) + + # create the header + customColor = QtGui.QColor(25, 175, 255) + labelItem = QtWidgets.QListWidgetItem("Joint") + labelItem.setFont(headerFont) + labelItem.setFlags(QtCore.Qt.NoItemFlags) + labelItem.setForeground(customColor) + labelItem.setBackground(QtCore.Qt.black) + labelItem.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.weightTableVertList.addItem(labelItem) + + # influence list + self.weightTableInfList = QtWidgets.QListWidget() + self.weightTableSplitter.addWidget(self.weightTableInfList) + + # create the header + labelValue = QtWidgets.QListWidgetItem("Avg. Weight") + labelValue.setFont(headerFont) + labelValue.setFlags(QtCore.Qt.NoItemFlags) + labelValue.setForeground(customColor) + labelValue.setBackground(QtCore.Qt.black) + labelValue.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.weightTableInfList.addItem(labelValue) + + # space + self.weightTableMainLayout.addSpacerItem( + QtWidgets.QSpacerItem(0, 50, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + # channel box layout frame + self.deformation_cbLayout = QtWidgets.QFrame() + self.deformation_cbLayout.setObjectName("dark") + + # set dimensions of channel box frame + self.deformation_cbLayout.setMinimumSize(QtCore.QSize(220, 500)) + self.deformation_cbLayout.setMaximumSize(QtCore.QSize(220, 900)) + self.deformation_hBoxLayout.addWidget(self.deformation_cbLayout) + + # add channel box layout + self.deformation_channelBoxLayout = QtWidgets.QVBoxLayout(self.deformation_cbLayout) + + # add the channel box from Maya to the UI + channelBoxWidget = cmds.channelBox(w=150, h=600) + pointer = mui.MQtUtil.findControl(channelBoxWidget) + self.channelBox = shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + self.deformation_channelBoxLayout.addWidget(self.channelBox) + self.channelBox.show() + self.channelBox.setObjectName("darkImg") + + self.deformation_channelBoxLayout.addSpacerItem( + QtWidgets.QSpacerItem(220, 500, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + # add the bottom buttons for Edit Setup and Build Rig + self.deformation_bottomButtonLayout = QtWidgets.QHBoxLayout() + self.deformation_bottomButtonLayout.setContentsMargins(25, 0, 0, 0) + + if attachToRigInterface: + # edit setup + self.deformation_editSetupBtn = QtWidgets.QPushButton("Edit Setup") + self.deformation_editSetupBtn.setMinimumHeight(50) + self.deformation_editSetupBtn.setMaximumHeight(50) + self.deformation_bottomButtonLayout.addWidget(self.deformation_editSetupBtn) + self.deformation_mainLayout.addLayout(self.deformation_bottomButtonLayout) + self.deformation_editSetupBtn.clicked.connect(self.mainUI.editSetup) + self.deformation_editSetupBtn.setObjectName("blueButtonSpecial") + self.deformation_editSetupBtn.setProperty("boldFont", True) + + # build rig + self.deformation_buildRigBtn = QtWidgets.QPushButton("Build Rig") + self.deformation_buildRigBtn.setMinimumHeight(50) + self.deformation_buildRigBtn.setMaximumHeight(50) + self.deformation_bottomButtonLayout.addWidget(self.deformation_buildRigBtn) + self.deformation_mainLayout.addLayout(self.deformation_bottomButtonLayout) + self.deformation_buildRigBtn.clicked.connect(self.mainUI.buildRig) + self.deformation_buildRigBtn.setObjectName("blueButtonSpecial") + self.deformation_buildRigBtn.setProperty("boldFont", True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # + # PAINT SKIN WEIGHTS # + # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # + + # create the vertical layout + self.psw_topLevelLayout = QtWidgets.QVBoxLayout(self.deformationPaintWeightsWidget) + + # create the scrollLayout + self.psw_paintWeightsScroll = QtWidgets.QScrollArea() + self.psw_topLevelLayout.addWidget(self.psw_paintWeightsScroll) + self.psw_paintWeightsScroll.setMinimumSize(QtCore.QSize(500, 500)) + self.psw_paintWeightsScroll.setMaximumSize(QtCore.QSize(500, 6000)) + self.psw_paintWeightsScroll.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) + self.psw_paintWeightsScroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + self.psw_paintWeightsScrollFrame = QtWidgets.QFrame(self.psw_paintWeightsScroll) + self.psw_paintWeightsScrollFrame.setObjectName("dark") + self.psw_paintWeightsScrollFrame.setMinimumSize(QtCore.QSize(500, 1100)) + self.psw_paintWeightsScrollFrame.setMaximumSize(QtCore.QSize(500, 6000)) + self.psw_paintWeightsScrollFrame.setSizePolicy( + QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)) + + self.psw_paintWeightsScroll.setWidget(self.psw_paintWeightsScrollFrame) + self.psw_mainLayout = QtWidgets.QVBoxLayout(self.psw_paintWeightsScrollFrame) + + # create the groupbox for influences + self.psw_groupBox = QtWidgets.QGroupBox("Influences") + self.psw_groupBox.setObjectName("light") + self.psw_groupBox.setMinimumSize(QtCore.QSize(470, 650)) + self.psw_groupBox.setMaximumSize(QtCore.QSize(470, 650)) + self.psw_groupBox.setFont(headerFont) + self.psw_mainLayout.addWidget(self.psw_groupBox) + self.psw_influenceLayout = QtWidgets.QVBoxLayout(self.psw_groupBox) + + # # # influence searches # # # + self.psw_influenceSearchesLayout = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_influenceSearchesLayout) + + self.psw_modSearchBar = QtWidgets.QLineEdit() + self.psw_modSearchBar.setPlaceholderText("Search Modules...") + self.psw_influenceSearchesLayout.addWidget(self.psw_modSearchBar) + self.psw_modSearchBar.textChanged.connect(self.paintSkinWeights_searchModules) + + self.psw_infSearchBar = QtWidgets.QLineEdit() + self.psw_infSearchBar.setPlaceholderText("Search Influences...") + self.psw_influenceSearchesLayout.addWidget(self.psw_infSearchBar) + self.psw_infSearchBar.textChanged.connect(self.paintSkinWeights_searchInfluences) + + # # # INFLUENCES # # # + self.psw_influenceListsLayout = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_influenceListsLayout) + + # module list + self.psw_moduleInfluenceList = QtWidgets.QListWidget() + self.psw_influenceListsLayout.addWidget(self.psw_moduleInfluenceList) + self.psw_moduleInfluenceList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.psw_moduleInfluenceList.itemSelectionChanged.connect(self.paintSkinWeights_populateInfs) + + # influence list + self.psw_influenceList = QtWidgets.QListWidget() + self.psw_influenceListsLayout.addWidget(self.psw_influenceList) + self.psw_influenceList.itemClicked.connect(self.paintSkinWeights_changeInf) + + # set context menu policy on influence list + self.psw_influenceList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.psw_influenceList.customContextMenuRequested.connect(self.paintSkinWeights_createContextMenu) + + # paint mode + self.psw_paintModeLayout = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_paintModeLayout) + modeLabel = QtWidgets.QLabel("Mode: ") + modeLabel.setStyleSheet("background: transparent;") + modeLabel.setFont(headerFont) + self.psw_paintModeLayout.addWidget(modeLabel) + + self.psw_paintModeButtonGrp = QtWidgets.QButtonGroup() + + self.psw_paintMode_paint = QtWidgets.QRadioButton("Paint") + self.psw_paintModeLayout.addWidget(self.psw_paintMode_paint) + self.psw_paintMode_paint.setChecked(True) + self.psw_paintMode_paint.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "skinPaintMode", str(1), self.psw_paintMode_paint)) + + self.psw_paintMode_select = QtWidgets.QRadioButton("Select") + self.psw_paintModeLayout.addWidget(self.psw_paintMode_select) + self.psw_paintMode_select.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "skinPaintMode", str(0), self.psw_paintMode_select)) + + self.psw_paintMode_paintSelect = QtWidgets.QRadioButton("Paint Select") + self.psw_paintModeLayout.addWidget(self.psw_paintMode_paintSelect) + self.psw_paintMode_paintSelect.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "skinPaintMode", str(2), self.psw_paintMode_paintSelect)) + + self.psw_paintModeButtonGrp.addButton(self.psw_paintMode_paint, 1) + self.psw_paintModeButtonGrp.addButton(self.psw_paintMode_select, 2) + self.psw_paintModeButtonGrp.addButton(self.psw_paintMode_paintSelect, 3) + + # paint select + self.psw_paintSelectLayout = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_paintSelectLayout) + paintSelectLabel = QtWidgets.QLabel("Paint Select: ") + paintSelectLabel.setStyleSheet("background: transparent;") + paintSelectLabel.setFont(headerFont) + self.psw_paintSelectLayout.addWidget(paintSelectLabel) + + self.psw_paintSelectButtonGrp = QtWidgets.QButtonGroup() + + self.psw_paintSelect_Add = QtWidgets.QRadioButton("Add") + self.psw_paintSelectLayout.addWidget(self.psw_paintSelect_Add) + self.psw_paintSelect_Add.setChecked(True) + self.psw_paintSelect_Add.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "paintSelectMode", str(1), self.psw_paintSelect_Add)) + + self.psw_paintSelect_Remove = QtWidgets.QRadioButton("Remove") + self.psw_paintSelectLayout.addWidget(self.psw_paintSelect_Remove) + self.psw_paintSelect_Remove.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "paintSelectMode", str(2), self.psw_paintSelect_Remove)) + + self.psw_paintSelect_Toggle = QtWidgets.QRadioButton("Toggle") + self.psw_paintSelectLayout.addWidget(self.psw_paintSelect_Toggle) + self.psw_paintSelect_Toggle.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "paintSelectMode", str(3), self.psw_paintSelect_Toggle)) + + self.psw_paintSelectButtonGrp.addButton(self.psw_paintSelect_Add, 1) + self.psw_paintSelectButtonGrp.addButton(self.psw_paintSelect_Remove, 2) + self.psw_paintSelectButtonGrp.addButton(self.psw_paintSelect_Toggle, 3) + + # paint operation + self.psw_paintOperationLayout = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_paintOperationLayout) + paintOperationLabel = QtWidgets.QLabel("Paint Operation: ") + paintOperationLabel.setStyleSheet("background: transparent;") + paintOperationLabel.setFont(headerFont) + self.psw_paintOperationLayout.addWidget(paintOperationLabel) + + self.psw_paintOperation = QtWidgets.QComboBox() + self.psw_paintOperationLayout.addWidget(self.psw_paintOperation) + self.psw_paintOperation.currentIndexChanged.connect( + partial(self.paintSkinWeights_updateCTX, "selectedattroper", "paintOp", self.psw_paintOperation)) + + self.psw_paintOperation.addItem("Add") + self.psw_paintOperation.addItem("Replace") + self.psw_paintOperation.addItem("Scale") + self.psw_paintOperation.addItem("Smooth") + + # paint profile + self.paintProfileButtons = [] + self.psw_paintProfileLayout = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_paintProfileLayout) + paintProfileLabel = QtWidgets.QLabel("Profile: ") + paintProfileLabel.setStyleSheet("background: transparent;") + paintProfileLabel.setFont(headerFont) + self.psw_paintProfileLayout.addWidget(paintProfileLabel) + + self.paintProfile_buttonGrp = QtWidgets.QButtonGroup() + + self.psw_paintProfile_Gaussian = QtWidgets.QPushButton() + self.psw_paintProfile_Gaussian.setMinimumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Gaussian.setMaximumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Gaussian.setIconSize(QtCore.QSize(30, 30)) + self.psw_paintProfileLayout.addWidget(self.psw_paintProfile_Gaussian) + self.psw_paintProfile_Gaussian.setCheckable(True) + img = utils.returnNicePath(self.iconsPath, "System/gaussian.png") + self.psw_paintProfile_Gaussian.clicked.connect( + partial(self.paintSkinWeights_togglePaintProfile, self.psw_paintProfile_Gaussian, img)) + self.paintProfileButtons.append([self.psw_paintProfile_Gaussian, img]) + self.psw_paintProfile_Gaussian.setChecked(True) + self.psw_paintProfile_Gaussian.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "stampProfile", "\"" + "gaussian" + "\"", + self.psw_paintProfile_Gaussian)) + + self.psw_paintProfile_Soft = QtWidgets.QPushButton() + self.psw_paintProfile_Soft.setMinimumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Soft.setMaximumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Soft.setIconSize(QtCore.QSize(30, 30)) + self.psw_paintProfileLayout.addWidget(self.psw_paintProfile_Soft) + self.psw_paintProfile_Soft.setCheckable(True) + img = utils.returnNicePath(self.iconsPath, "System/soft.png") + self.psw_paintProfile_Soft.clicked.connect( + partial(self.paintSkinWeights_togglePaintProfile, self.psw_paintProfile_Soft, img)) + self.paintProfileButtons.append([self.psw_paintProfile_Soft, img]) + self.psw_paintProfile_Soft.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "stampProfile", "\"" + "poly" + "\"", self.psw_paintProfile_Soft)) + + self.psw_paintProfile_Solid = QtWidgets.QPushButton() + self.psw_paintProfile_Solid.setMinimumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Solid.setMaximumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Solid.setIconSize(QtCore.QSize(30, 30)) + self.psw_paintProfileLayout.addWidget(self.psw_paintProfile_Solid) + self.psw_paintProfile_Solid.setCheckable(True) + img = utils.returnNicePath(self.iconsPath, "System/solid.png") + self.psw_paintProfile_Solid.clicked.connect( + partial(self.paintSkinWeights_togglePaintProfile, self.psw_paintProfile_Solid, img)) + self.paintProfileButtons.append([self.psw_paintProfile_Solid, img]) + self.psw_paintProfile_Solid.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "stampProfile", "\"" + "solid" + "\"", + self.psw_paintProfile_Solid)) + + self.psw_paintProfile_Square = QtWidgets.QPushButton() + self.psw_paintProfile_Square.setMinimumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Square.setMaximumSize(QtCore.QSize(35, 35)) + self.psw_paintProfile_Square.setIconSize(QtCore.QSize(30, 30)) + self.psw_paintProfileLayout.addWidget(self.psw_paintProfile_Square) + self.psw_paintProfile_Square.setCheckable(True) + img = utils.returnNicePath(self.iconsPath, "System/square.png") + self.psw_paintProfile_Square.clicked.connect( + partial(self.paintSkinWeights_togglePaintProfile, self.psw_paintProfile_Square, img)) + self.paintProfileButtons.append([self.psw_paintProfile_Square, img]) + self.psw_paintProfile_Square.clicked.connect( + partial(self.paintSkinWeights_updateCTX, "stampProfile", "\"" + "square" + "\"", + self.psw_paintProfile_Square)) + + self.paintProfile_buttonGrp.addButton(self.psw_paintProfile_Gaussian, 1) + self.paintProfile_buttonGrp.addButton(self.psw_paintProfile_Soft, 2) + self.paintProfile_buttonGrp.addButton(self.psw_paintProfile_Solid, 3) + self.paintProfile_buttonGrp.addButton(self.psw_paintProfile_Square, 4) + + # opacity + line = QtWidgets.QFrame() + line.setMinimumSize(400, 3) + line.setFrameShape(QtWidgets.QFrame.HLine) + line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.psw_influenceLayout.addWidget(line) + + self.psw_opacityLayoutTop = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_opacityLayoutTop) + + opacLabel = QtWidgets.QLabel("Opacity: ") + opacLabel.setStyleSheet("background: transparent;") + opacLabel.setFont(headerFont) + self.psw_opacityLayoutTop.addWidget(opacLabel) + + # # # opacity slider # # # + self.psw_opacitySlider = QtWidgets.QSlider() + self.psw_opacityLayoutTop.addWidget(self.psw_opacitySlider) + self.psw_opacitySlider.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.psw_opacitySlider.setRange(0, 100) + self.psw_opacitySlider.setValue(100) + self.psw_opacitySlider.setSingleStep(1) + self.psw_opacitySlider.setPageStep(1) + self.psw_opacitySlider.setTickPosition(QtWidgets.QSlider.TicksAbove) + self.psw_opacitySlider.setTickInterval(20) + + # # # opacity field # # # + self.psw_opacityField = QtWidgets.QDoubleSpinBox() + self.psw_opacityLayoutTop.addWidget(self.psw_opacityField) + self.psw_opacityField.setValue(1.00) + self.psw_opacityField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.psw_opacityField.setReadOnly(True) + + # # # opacity signals/slots # # # + self.psw_opacitySlider.valueChanged.connect( + partial(self.paintSkinWeights_UpdateSliders, self.psw_opacitySlider, self.psw_opacityField, True)) + self.psw_opacitySlider.valueChanged.connect( + partial(self.paintSkinWeights_updateCTX, "opacity", "sliderEdit", self.psw_opacitySlider)) + + # # # opacity presets # # # + self.psw_opacityLayoutBottom = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_opacityLayoutBottom) + + label = QtWidgets.QLabel("Presets: ") + label.setStyleSheet("background: transparent;") + label.setFont(headerFont) + self.psw_opacityLayoutBottom.addWidget(label) + + self.psw_opacPreset1 = QtWidgets.QPushButton("0.00") + self.psw_opacPreset1.setMaximumSize(40, 20) + self.psw_opacPreset1.setMinimumSize(40, 20) + self.psw_opacityLayoutBottom.addWidget(self.psw_opacPreset1) + self.psw_opacPreset1.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_opacitySlider, 0)) + self.psw_opacPreset1.setObjectName("blueButton") + self.psw_opacPreset1.setMinimumHeight(25) + + self.psw_opacPreset2 = QtWidgets.QPushButton("0.10") + self.psw_opacPreset2.setMaximumSize(40, 20) + self.psw_opacPreset2.setMinimumSize(40, 20) + self.psw_opacityLayoutBottom.addWidget(self.psw_opacPreset2) + self.psw_opacPreset2.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_opacitySlider, 10)) + self.psw_opacPreset2.setObjectName("blueButton") + self.psw_opacPreset2.setMinimumHeight(25) + + self.psw_opacPreset3 = QtWidgets.QPushButton("0.25") + self.psw_opacPreset3.setMaximumSize(40, 20) + self.psw_opacPreset3.setMinimumSize(40, 20) + self.psw_opacityLayoutBottom.addWidget(self.psw_opacPreset3) + self.psw_opacPreset3.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_opacitySlider, 25)) + self.psw_opacPreset3.setObjectName("blueButton") + self.psw_opacPreset3.setMinimumHeight(25) + + self.psw_opacPreset4 = QtWidgets.QPushButton("0.50") + self.psw_opacPreset4.setMaximumSize(40, 20) + self.psw_opacPreset4.setMinimumSize(40, 20) + self.psw_opacityLayoutBottom.addWidget(self.psw_opacPreset4) + self.psw_opacPreset4.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_opacitySlider, 50)) + self.psw_opacPreset4.setObjectName("blueButton") + self.psw_opacPreset4.setMinimumHeight(25) + + self.psw_opacPreset5 = QtWidgets.QPushButton("0.75") + self.psw_opacPreset5.setMaximumSize(40, 20) + self.psw_opacPreset5.setMinimumSize(40, 20) + self.psw_opacityLayoutBottom.addWidget(self.psw_opacPreset5) + self.psw_opacPreset5.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_opacitySlider, 75)) + self.psw_opacPreset5.setObjectName("blueButton") + self.psw_opacPreset5.setMinimumHeight(25) + + self.psw_opacPreset6 = QtWidgets.QPushButton("1.00") + self.psw_opacPreset6.setMaximumSize(40, 20) + self.psw_opacPreset6.setMinimumSize(40, 20) + self.psw_opacityLayoutBottom.addWidget(self.psw_opacPreset6) + self.psw_opacPreset6.clicked.connect( + partial(self.paintSkinWeights_SetSliderValues, self.psw_opacitySlider, 100)) + self.psw_opacPreset6.setObjectName("blueButton") + self.psw_opacPreset6.setMinimumHeight(25) + + self.psw_opacityLayoutBottom.setSpacing(20) + self.psw_opacityLayoutBottom.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)) + + # value + line = QtWidgets.QFrame() + line.setMinimumSize(400, 3) + line.setFrameShape(QtWidgets.QFrame.HLine) + line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.psw_influenceLayout.addWidget(line) + + self.psw_valueLayoutTop = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_valueLayoutTop) + + valueLabel = QtWidgets.QLabel("Value: ") + valueLabel.setStyleSheet("background: transparent;") + valueLabel.setFont(headerFont) + self.psw_valueLayoutTop.addWidget(valueLabel) + + # # # value slider # # # + self.psw_valueSlider = QtWidgets.QSlider() + self.psw_valueLayoutTop.addWidget(self.psw_valueSlider) + self.psw_valueSlider.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.psw_valueSlider.setRange(0, 100) + self.psw_valueSlider.setValue(100) + self.psw_valueSlider.setSingleStep(1) + self.psw_valueSlider.setPageStep(1) + self.psw_valueSlider.setTickPosition(QtWidgets.QSlider.TicksAbove) + self.psw_valueSlider.setTickInterval(20) + + # # # value field # # # + self.psw_valueField = QtWidgets.QDoubleSpinBox() + self.psw_valueLayoutTop.addWidget(self.psw_valueField) + self.psw_valueField.setValue(1.00) + self.psw_valueField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.psw_valueField.setReadOnly(True) + + # # # value signals/slots # # # + self.psw_valueSlider.valueChanged.connect( + partial(self.paintSkinWeights_UpdateSliders, self.psw_valueSlider, self.psw_valueField, True)) + self.psw_valueSlider.valueChanged.connect( + partial(self.paintSkinWeights_updateCTX, "value", "sliderEdit", self.psw_valueSlider)) + + # # # value presets # # # + self.psw_valueLayoutBottom = QtWidgets.QHBoxLayout() + self.psw_influenceLayout.addLayout(self.psw_valueLayoutBottom) + + label = QtWidgets.QLabel("Presets: ") + label.setStyleSheet("background: transparent;") + label.setFont(headerFont) + self.psw_valueLayoutBottom.addWidget(label) + + self.psw_valuePreset1 = QtWidgets.QPushButton("0.00") + self.psw_valuePreset1.setMaximumSize(40, 20) + self.psw_valuePreset1.setMinimumSize(40, 20) + self.psw_valueLayoutBottom.addWidget(self.psw_valuePreset1) + self.psw_valuePreset1.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_valueSlider, 0)) + self.psw_valuePreset1.setObjectName("blueButton") + self.psw_valuePreset1.setMinimumHeight(25) + + self.psw_valuePreset2 = QtWidgets.QPushButton("0.10") + self.psw_valuePreset2.setMaximumSize(40, 20) + self.psw_valuePreset2.setMinimumSize(40, 20) + self.psw_valueLayoutBottom.addWidget(self.psw_valuePreset2) + self.psw_valuePreset2.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_valueSlider, 10)) + self.psw_valuePreset2.setObjectName("blueButton") + self.psw_valuePreset2.setMinimumHeight(25) + + self.psw_valuePreset3 = QtWidgets.QPushButton("0.25") + self.psw_valuePreset3.setMaximumSize(40, 20) + self.psw_valuePreset3.setMinimumSize(40, 20) + self.psw_valueLayoutBottom.addWidget(self.psw_valuePreset3) + self.psw_valuePreset3.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_valueSlider, 25)) + self.psw_valuePreset3.setObjectName("blueButton") + self.psw_valuePreset3.setMinimumHeight(25) + + self.psw_valuePreset4 = QtWidgets.QPushButton("0.50") + self.psw_valuePreset4.setMaximumSize(40, 20) + self.psw_valuePreset4.setMinimumSize(40, 20) + self.psw_valueLayoutBottom.addWidget(self.psw_valuePreset4) + self.psw_valuePreset4.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_valueSlider, 50)) + self.psw_valuePreset4.setObjectName("blueButton") + self.psw_valuePreset4.setMinimumHeight(25) + + self.psw_valuePreset5 = QtWidgets.QPushButton("0.75") + self.psw_valuePreset5.setMaximumSize(40, 20) + self.psw_valuePreset5.setMinimumSize(40, 20) + self.psw_valueLayoutBottom.addWidget(self.psw_valuePreset5) + self.psw_valuePreset5.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_valueSlider, 75)) + self.psw_valuePreset5.setObjectName("blueButton") + self.psw_valuePreset5.setMinimumHeight(25) + + self.psw_valuePreset6 = QtWidgets.QPushButton("1.00") + self.psw_valuePreset6.setMaximumSize(40, 20) + self.psw_valuePreset6.setMinimumSize(40, 20) + self.psw_valueLayoutBottom.addWidget(self.psw_valuePreset6) + self.psw_valuePreset6.clicked.connect(partial(self.paintSkinWeights_SetSliderValues, self.psw_valueSlider, 100)) + self.psw_valuePreset6.setObjectName("blueButton") + self.psw_valuePreset6.setMinimumHeight(25) + + self.psw_valueLayoutBottom.setSpacing(20) + self.psw_valueLayoutBottom.addSpacerItem( + QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)) + + # flood weights + self.psw_floodWeightsBtn = QtWidgets.QPushButton("Flood Weights") + self.psw_floodWeightsBtn.setFont(headerFont) + self.psw_floodWeightsBtn.setMaximumHeight(40) + self.psw_floodWeightsBtn.setMinimumHeight(40) + self.psw_influenceLayout.addWidget(self.psw_floodWeightsBtn) + self.psw_floodWeightsBtn.clicked.connect(self.paintSkinWeights_floodWeights) + self.psw_floodWeightsBtn.setObjectName("blueButton") + + # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # + # create the groupbox for stroke # + # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # + self.psw_groupBoxStroke = QtWidgets.QGroupBox("Stroke") + self.psw_groupBoxStroke.setObjectName("light") + self.psw_groupBoxStroke.setMinimumSize(QtCore.QSize(470, 150)) + self.psw_groupBoxStroke.setMaximumSize(QtCore.QSize(470, 150)) + self.psw_groupBoxStroke.setFont(headerFont) + self.psw_mainLayout.addWidget(self.psw_groupBoxStroke) + self.psw_strokeLayout = QtWidgets.QVBoxLayout(self.psw_groupBoxStroke) + + # # # radius(U) # # # + self.psw_radiusULayout = QtWidgets.QHBoxLayout() + self.psw_strokeLayout.addLayout(self.psw_radiusULayout) + + radiusULabel = QtWidgets.QLabel("Radius(U): ") + radiusULabel.setStyleSheet("background: transparent;") + radiusULabel.setFont(headerFont) + self.psw_radiusULayout.addWidget(radiusULabel) + + # # # radius(U) slider # # # + self.psw_radiusUSlider = QtWidgets.QSlider() + self.psw_radiusULayout.addWidget(self.psw_radiusUSlider) + self.psw_radiusUSlider.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.psw_radiusUSlider.setRange(0, 50) + self.psw_radiusUSlider.setValue(10) + self.psw_radiusUSlider.setSingleStep(1) + self.psw_radiusUSlider.setPageStep(1) + self.psw_radiusUSlider.setTickPosition(QtWidgets.QSlider.TicksAbove) + self.psw_radiusUSlider.setTickInterval(10) + + # # # radius(U) field # # # + self.psw_radiusUField = QtWidgets.QDoubleSpinBox() + self.psw_radiusULayout.addWidget(self.psw_radiusUField) + self.psw_radiusUField.setValue(10.00) + self.psw_radiusUField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.psw_radiusUField.setReadOnly(True) + + # # # radius(U) signals/slots # # # + self.psw_radiusUSlider.valueChanged.connect( + partial(self.paintSkinWeights_UpdateSliders, self.psw_radiusUSlider, self.psw_radiusUField, False)) + self.psw_radiusUSlider.valueChanged.connect( + partial(self.paintSkinWeights_updateCTX, "radius", "slider", self.psw_radiusUSlider)) + + # # # radius(L) # # # + self.psw_radiuslLayout = QtWidgets.QHBoxLayout() + self.psw_strokeLayout.addLayout(self.psw_radiuslLayout) + + radiuslLabel = QtWidgets.QLabel("Radius(L): ") + radiuslLabel.setStyleSheet("background: transparent;") + radiuslLabel.setFont(headerFont) + self.psw_radiuslLayout.addWidget(radiuslLabel) + + # # # radius(L) slider # # # + self.psw_radiuslSlider = QtWidgets.QSlider() + self.psw_radiuslLayout.addWidget(self.psw_radiuslSlider) + self.psw_radiuslSlider.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.psw_radiuslSlider.setRange(0, 50) + self.psw_radiuslSlider.setValue(0) + self.psw_radiuslSlider.setSingleStep(1) + self.psw_radiuslSlider.setPageStep(1) + self.psw_radiuslSlider.setTickPosition(QtWidgets.QSlider.TicksAbove) + self.psw_radiuslSlider.setTickInterval(10) + + # # # radius(L) field # # # + self.psw_radiuslField = QtWidgets.QDoubleSpinBox() + self.psw_radiuslLayout.addWidget(self.psw_radiuslField) + self.psw_radiuslField.setValue(0.00) + self.psw_radiuslField.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.psw_radiuslField.setReadOnly(True) + + # # # radius(L) signals/slots # # # + self.psw_radiuslSlider.valueChanged.connect( + partial(self.paintSkinWeights_UpdateSliders, self.psw_radiuslSlider, self.psw_radiuslField, False)) + self.psw_radiuslSlider.valueChanged.connect( + partial(self.paintSkinWeights_updateCTX, "lowerradius", "slider", self.psw_radiuslSlider)) + + # # # reflection # # # + self.psw_reflectionTopLayout = QtWidgets.QHBoxLayout() + self.psw_strokeLayout.addLayout(self.psw_reflectionTopLayout) + + self.psw_reflectionToggleCB = QtWidgets.QCheckBox("Reflection") + self.psw_reflectionTopLayout.addWidget(self.psw_reflectionToggleCB) + self.psw_reflectionToggleCB.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "reflection", None, self.psw_reflectionToggleCB)) + + self.psw_reflectionBottomLayout = QtWidgets.QHBoxLayout() + self.psw_strokeLayout.addLayout(self.psw_reflectionBottomLayout) + + reflectLabel = QtWidgets.QLabel("Reflection Axis: ") + reflectLabel.setStyleSheet("background: transparent;") + reflectLabel.setFont(headerFont) + self.psw_reflectionBottomLayout.addWidget(reflectLabel) + + self.psw_reflectAxisX = QtWidgets.QCheckBox("X") + self.psw_reflectionBottomLayout.addWidget(self.psw_reflectAxisX) + self.psw_reflectAxisX.setAutoExclusive(True) + self.psw_reflectAxisX.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "reflectionaxis", "\"" + "x" + "\"", self.psw_reflectAxisX)) + + self.psw_reflectAxisY = QtWidgets.QCheckBox("Y") + self.psw_reflectionBottomLayout.addWidget(self.psw_reflectAxisY) + self.psw_reflectAxisY.setAutoExclusive(True) + self.psw_reflectAxisY.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "reflectionaxis", "\"" + "y" + "\"", self.psw_reflectAxisY)) + + self.psw_reflectAxisZ = QtWidgets.QCheckBox("Z") + self.psw_reflectionBottomLayout.addWidget(self.psw_reflectAxisZ) + self.psw_reflectAxisZ.setAutoExclusive(True) + self.psw_reflectAxisZ.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "reflectionaxis", "\"" + "z" + "\"", self.psw_reflectAxisZ)) + + # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # + # create the groupbox for stylus pressure # + # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # + self.psw_groupBoxStylus = QtWidgets.QGroupBox("Stylus Pressure") + self.psw_groupBoxStylus.setObjectName("light") + self.psw_groupBoxStylus.setMinimumSize(QtCore.QSize(470, 100)) + self.psw_groupBoxStylus.setMaximumSize(QtCore.QSize(470, 100)) + self.psw_groupBoxStylus.setFont(headerFont) + self.psw_mainLayout.addWidget(self.psw_groupBoxStylus) + self.psw_stylusLayout = QtWidgets.QVBoxLayout(self.psw_groupBoxStylus) + + self.psw_stylusPressureLayout = QtWidgets.QHBoxLayout() + self.psw_stylusLayout.addLayout(self.psw_stylusPressureLayout) + + self.psw_stylusPressureToggle = QtWidgets.QCheckBox("Stylus Pressure") + self.psw_stylusPressureLayout.addWidget(self.psw_stylusPressureToggle) + self.psw_stylusPressureToggle.setChecked(True) + self.psw_stylusPressureToggle.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "usepressure", None, self.psw_stylusPressureToggle)) + + spLabel = QtWidgets.QLabel("Pressure Mapping: ") + spLabel.setStyleSheet("background: transparent;") + spLabel.setFont(headerFont) + self.psw_stylusPressureLayout.addWidget(spLabel) + + self.psw_stylusPressureOptions = QtWidgets.QComboBox() + self.psw_stylusPressureLayout.addWidget(self.psw_stylusPressureOptions) + self.psw_stylusPressureOptions.currentIndexChanged.connect( + partial(self.paintSkinWeights_updateCTX, "mappressure", "mapping", self.psw_stylusPressureOptions)) + self.psw_stylusPressureOptions.addItem("Opacity") + self.psw_stylusPressureOptions.addItem("Radius") + self.psw_stylusPressureOptions.addItem("Both") + + # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # + # create the groupbox for display settings # + # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # + self.psw_groupBoxDisplay = QtWidgets.QGroupBox("Display") + self.psw_groupBoxDisplay.setObjectName("light") + self.psw_groupBoxDisplay.setMinimumSize(QtCore.QSize(470, 150)) + self.psw_groupBoxDisplay.setMaximumSize(QtCore.QSize(470, 150)) + self.psw_groupBoxDisplay.setFont(headerFont) + self.psw_mainLayout.addWidget(self.psw_groupBoxDisplay) + self.psw_displayLayout = QtWidgets.QVBoxLayout(self.psw_groupBoxDisplay) + + # # # top settings # # # + self.psw_displaySettingsTop = QtWidgets.QHBoxLayout() + self.psw_displayLayout.addLayout(self.psw_displaySettingsTop) + + self.psw_displayDrawBrush = QtWidgets.QCheckBox("Draw Brush") + self.psw_displaySettingsTop.addWidget(self.psw_displayDrawBrush) + self.psw_displayDrawBrush.setChecked(True) + self.psw_displayDrawBrush.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "outline", None, self.psw_displayDrawBrush)) + + self.psw_displayDrawBrushPaint = QtWidgets.QCheckBox("Draw Brush While Painting") + self.psw_displaySettingsTop.addWidget(self.psw_displayDrawBrushPaint) + self.psw_displayDrawBrushPaint.setChecked(True) + self.psw_displayDrawBrushPaint.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "outwhilepaint", None, self.psw_displayDrawBrushPaint)) + + # # # mid settings # # # + self.psw_displaySettingsMid = QtWidgets.QHBoxLayout() + self.psw_displayLayout.addLayout(self.psw_displaySettingsMid) + + self.psw_displayDrawBrushTangent = QtWidgets.QCheckBox("Draw Brush Tangent Outline") + self.psw_displaySettingsMid.addWidget(self.psw_displayDrawBrushTangent) + self.psw_displayDrawBrushTangent.setChecked(True) + self.psw_displayDrawBrushTangent.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "tangentOutline", None, self.psw_displayDrawBrushTangent)) + + self.psw_displayDrawBrushFeedback = QtWidgets.QCheckBox("Draw Brush Feedback") + self.psw_displaySettingsMid.addWidget(self.psw_displayDrawBrushFeedback) + self.psw_displayDrawBrushFeedback.setChecked(True) + self.psw_displayDrawBrushFeedback.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "brushfeedback", None, self.psw_displayDrawBrushFeedback)) + + # # # bottom settings # # # + self.psw_displaySettingsBot1 = QtWidgets.QHBoxLayout() + self.psw_displayLayout.addLayout(self.psw_displaySettingsBot1) + + self.psw_displayShowWires = QtWidgets.QCheckBox("Show Wireframe") + self.psw_displaySettingsBot1.addWidget(self.psw_displayShowWires) + self.psw_displayShowWires.setChecked(True) + self.psw_displayShowWires.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "showactive", None, self.psw_displayShowWires)) + + self.psw_displayColorFeedback = QtWidgets.QCheckBox("Color Feedback") + self.psw_displaySettingsBot1.addWidget(self.psw_displayColorFeedback) + self.psw_displayColorFeedback.setChecked(True) + self.psw_displayColorFeedback.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "colorfeedback", None, self.psw_displayColorFeedback)) + + # # # bottom settings 2 # # # + self.psw_displaySettingsBot2 = QtWidgets.QHBoxLayout() + self.psw_displayLayout.addLayout(self.psw_displaySettingsBot2) + + self.psw_displayXRayJoints = QtWidgets.QCheckBox("X-Ray Joints") + self.psw_displaySettingsBot2.addWidget(self.psw_displayXRayJoints) + self.psw_displayXRayJoints.setChecked(False) + self.psw_displayXRayJoints.stateChanged.connect( + partial(self.paintSkinWeights_updateCTX, "xrayJoints", None, self.psw_displayXRayJoints)) + + # spacer + self.psw_mainLayout.addSpacerItem( + QtWidgets.QSpacerItem(500, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_scriptJob(self): + # create selection script job for populating weight table + + jobs = cmds.scriptJob(listJobs=True) + try: + if self.wtScriptJob in jobs: + cmds.scriptJob(kill=self.wtScriptJob, force=True) + except: + pass + + # create the script job + self.wtScriptJob = cmds.scriptJob(event=["SelectionChanged", self.weightTable_getInfs], kws=True) + return self.wtScriptJob + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_paintWeightsMode(self): + + # paint weights mode + mel.eval("ArtPaintSkinWeightsTool;") + + # change current index of the stack widget + self.defomation_mainWidget.setCurrentIndex(1) + + # kick off script job to monitor when tool changes + self.paintWeightScriptJob = cmds.scriptJob(event=["ToolChanged", self.paintSkinWeightsScriptJob], runOnce=True, + kws=True) + + # check artAttrSkinPaintCtx settings + currentCtx = cmds.currentCtx() + + # # # influences # # # + skinPaintMode = cmds.artAttrSkinPaintCtx(currentCtx, q=True, skinPaintMode=True) + paintSelectMode = cmds.artAttrSkinPaintCtx(currentCtx, q=True, paintSelectMode=True) + selectedattroper = cmds.artAttrSkinPaintCtx(currentCtx, q=True, selectedattroper=True) + stampProfile = cmds.artAttrSkinPaintCtx(currentCtx, q=True, stampProfile=True) + opacity = cmds.artAttrSkinPaintCtx(currentCtx, q=True, opacity=True) + value = cmds.artAttrSkinPaintCtx(currentCtx, q=True, value=True) + + if skinPaintMode == 0: + self.psw_paintMode_select.setChecked(True) + if skinPaintMode == 1: + self.psw_paintMode_paint.setChecked(True) + if skinPaintMode == 2: + self.psw_paintMode_paintSelect.setChecked(True) + + if paintSelectMode == 1: + self.psw_paintSelect_Add.setChecked(True) + if paintSelectMode == 2: + self.psw_paintSelect_Remove.setChecked(True) + if paintSelectMode == 3: + self.psw_paintSelect_Toggle.setChecked(True) + + if selectedattroper == "additive": + self.psw_paintOperation.setCurrentIndex(0) + if selectedattroper == "absolute": + self.psw_paintOperation.setCurrentIndex(1) + if selectedattroper == "scale": + self.psw_paintOperation.setCurrentIndex(21) + if selectedattroper == "smooth": + self.psw_paintOperation.setCurrentIndex(3) + + if stampProfile == "gaussian": + self.psw_paintProfile_Gaussian.setChecked(True) + self.psw_paintProfile_Gaussian.click() + if stampProfile == "soft": + self.psw_paintProfile_Soft.setChecked(True) + self.psw_paintProfile_Soft.click() + if stampProfile == "solid": + self.psw_paintProfile_Solid.setChecked(True) + self.psw_paintProfile_Solid.click() + if stampProfile == "square": + self.psw_paintProfile_Square.setChecked(True) + self.psw_paintProfile_Square.click() + + self.psw_opacitySlider.setValue(opacity * 100) + self.psw_valueSlider.setValue(value * 100) + + # stroke + radius = cmds.artAttrSkinPaintCtx(currentCtx, q=True, radius=True) + lowerRadius = cmds.artAttrSkinPaintCtx(currentCtx, q=True, lowerradius=True) + reflection = cmds.artAttrSkinPaintCtx(currentCtx, q=True, reflection=True) + reflectionaxis = cmds.artAttrSkinPaintCtx(currentCtx, q=True, reflectionaxis=True) + + self.psw_radiusUSlider.setValue(int(radius)) + self.psw_radiuslSlider.setValue(lowerRadius) + self.psw_reflectionToggleCB.setChecked(reflection) + + if reflectionaxis == "x": + self.psw_reflectAxisX.setChecked(True) + if reflectionaxis == "y": + self.psw_reflectAxisY.setChecked(True) + if reflectionaxis == "z": + self.psw_reflectAxisZ.setChecked(True) + + # stylus pressure + usepressure = cmds.artAttrSkinPaintCtx(currentCtx, q=True, usepressure=True) + mappressure = cmds.artAttrSkinPaintCtx(currentCtx, q=True, mappressure=True) + + self.psw_stylusPressureToggle.setChecked(usepressure) + + if mappressure == "Opacity": + self.psw_stylusPressureOptions.setCurrentIndex(0) + if mappressure == "Radius": + self.psw_stylusPressureOptions.setCurrentIndex(1) + if mappressure == "Both": + self.psw_stylusPressureOptions.setCurrentIndex(2) + + # display + outline = cmds.artAttrSkinPaintCtx(currentCtx, q=True, outline=True) + outwhilepaint = cmds.artAttrSkinPaintCtx(currentCtx, q=True, outwhilepaint=True) + tangentOutline = cmds.artAttrSkinPaintCtx(currentCtx, q=True, tangentOutline=True) + brushfeedback = cmds.artAttrSkinPaintCtx(currentCtx, q=True, brushfeedback=True) + showactive = cmds.artAttrSkinPaintCtx(currentCtx, q=True, showactive=True) + colorfeedback = cmds.artAttrSkinPaintCtx(currentCtx, q=True, colorfeedback=True) + xrayJoints = cmds.artAttrSkinPaintCtx(currentCtx, q=True, xrayJoints=True) + + self.psw_displayDrawBrush.setChecked(outline) + self.psw_displayDrawBrushPaint.setChecked(outwhilepaint) + self.psw_displayDrawBrushTangent.setChecked(tangentOutline) + self.psw_displayDrawBrushFeedback.setChecked(brushfeedback) + self.psw_displayShowWires.setChecked(showactive) + self.psw_displayColorFeedback.setChecked(colorfeedback) + self.psw_displayXRayJoints.setChecked(xrayJoints) + + # populate influence lists + self.paintSkinWeights_populateModules() + self.paintSkinWeights_populateInfs() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeightsScriptJob(self): + + # check current context + currentTool = cmds.currentCtx() + + # if current context is not artAttrSkinCtx, setCurrentIndex back to 0 + if currentTool != "artAttrSkinContext": + self.defomation_mainWidget.setCurrentIndex(0) + + currentSelection = cmds.ls(sl=True) + cmds.select(clear=True) + cmds.select(currentSelection) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_populateModules(self): + + # clear list + self.psw_moduleInfluenceList.clear() + + # add modules to list + modules = utils.returnRigModules() + for module in modules: + name = cmds.getAttr(module + ".moduleName") + self.psw_moduleInfluenceList.addItem(name) + + # select all modules, populating the entire influence list + self.psw_moduleInfluenceList.selectAll() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeights_populateInfs(self): + + # find the selected mesh + selection = cmds.ls(sl=True) + + # clear influences in list + influences = [] + self.psw_influenceList.clear() + + # check if valid selection + if len(selection) >= 1: + mesh = selection[0] + if cmds.nodeType(mesh) == "transform": + + # check for skinCluster + skinClusters = cmds.ls(type='skinCluster') + + # go through each found skin cluster, and if we find a skin cluster whose geometry matches our selection + # get influences + for cluster in skinClusters: + geometry = cmds.skinCluster(cluster, q=True, g=True)[0] + geoTransform = cmds.listRelatives(geometry, parent=True)[0] + if geoTransform == mesh: + skinCluster = cluster + influences = cmds.skinCluster(cluster, q=True, inf=True) + + # compare influences in list to selected modules + createdJoints = [] + modules = utils.returnRigModules() + selectedModules = self.psw_moduleInfluenceList.selectedItems() + + for each in selectedModules: + selectedText = each.text() + for module in modules: + modName = cmds.getAttr(module + ".moduleName") + if modName == selectedText: + # get joints of that module + createdBones = cmds.getAttr(module + ".Created_Bones") + splitJoints = createdBones.split("::") + + for bone in splitJoints: + if bone != "": + createdJoints.append(bone) + + # add the influneces to the list + if len(influences) > 0: + for inf in influences: + if inf in createdJoints: + lockColor = QtGui.QColor(0, 0, 0) + lockBkgrd = QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense2Pattern) + lockFont = QtGui.QFont() + lockFont.setPointSize(8) + lockFont.setBold(True) + + item = QtWidgets.QListWidgetItem(inf) + locked = cmds.skinCluster(self.skinCluster, q=True, inf=inf, lw=True) + if locked: + item.setFont(lockFont) + item.setForeground(lockColor) + item.setBackground(lockBkgrd) + self.psw_influenceList.addItem(item) + + # auto select the first item in the list + self.psw_influenceList.setCurrentRow(0) + self.paintSkinWeights_changeInf() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_changeInf(self): + try: + selected = self.psw_influenceList.selectedItems() + selected = selected[0].text() + currentCtx = cmds.currentCtx() + cmds.artAttrSkinPaintCtx(currentCtx, edit=True, influence=selected) + + # loop through all of the influences in the list and set the tool to not have them selected + for i in range(self.psw_influenceList.count()): + item = self.psw_influenceList.item(i) + influence = item.text() + string = "artSkinInflListChanging " + "\"" + influence + "\"" + " 0;" + mel.eval(string) + string = "artSkinInflListChanged artAttrSkinPaintCtx;" + mel.eval(string) + + # turn on the selected influence + string = "artSkinInflListChanging " + "\"" + selected + "\"" + " 1;" + mel.eval(string) + string = "artSkinInflListChanged artAttrSkinPaintCtx;" + mel.eval(string) + except: + pass + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_mirrorSkinWeights(self): + + # get the current selection + selection = cmds.ls(sl=True, flatten=True) + + # if the whole object is selected, use Maya's mirroring + if selection[0].find(".vtx") == -1: + mel.eval("MirrorSkinWeightsOptions;") + + # if holding shift, use Maya's mirroring: + mods = cmds.getModifiers() + if (mods & 1) > 0: + mel.eval("MirrorSkinWeightsOptions;") + + # otherwise, use custom mirroring + else: + # get selection and determine if selection is an object or component selection + selectionType = None + + if len(selection) > 0: + if selection[0].find(".vtx") != -1: + selectionType = "vertex" + selectedVerts = selection + # find the object name + objectName = selection[0].partition(".vtx")[0] + + else: + cmds.warning("No vertices selected.") + return + + # find total number of vertices in object + totalNumVerts = cmds.polyEvaluate(objectName, v=True) + cmds.select(objectName + ".vtx[*]") + allVerts = cmds.ls(sl=True, flatten=True) + mirrorVertices = [] + + # find all joints in the object's skinCluster + skinClusters = cmds.ls(type='skinCluster') + + # go through each found skin cluster, and if we find a skin cluster whose geometry matches our selection, get influences + for cluster in skinClusters: + geometry = cmds.skinCluster(cluster, q=True, g=True)[0] + geoTransform = cmds.listRelatives(geometry, parent=True)[0] + if geoTransform == objectName: + skinCluster = cluster + influences = cmds.skinCluster(cluster, q=True, inf=True) + + # create the master list that will hold the vertex, its mirror, the influences of the vertex, the influences of the mirror, and the values + masterInfDataList = [] + mirrorVertex = None + + for vert in selectedVerts: + pos = cmds.pointPosition(vert) + mirrorVertPosition = [pos[0] * -1, pos[1], pos[2]] + mirrorVertPosX = mirrorVertPosition[0] * 1000 + mirrorVertPosX = math.floor(mirrorVertPosX) + + for i in range(totalNumVerts): + testPos = cmds.pointPosition(allVerts[i]) + if testPos == mirrorVertPosition: + mirrorVertex = allVerts[i] + mirrorVertices.append(mirrorVertex) + + # get this vert's influence information + vertexInfs = cmds.skinPercent(skinCluster, vert, q=True, ignoreBelow=0.0001, transform=None) + infValues = cmds.skinPercent(skinCluster, vert, q=True, ignoreBelow=0.0001, value=True) + + # get the world position of the vertexInfs + mirrorInfs = [] + for inf in vertexInfs: + worldPos = cmds.xform(inf, q=True, ws=True, t=True) + + # try to get the mirror influence + mirrorPos = [round(worldPos[0] * -1, 2), round(worldPos[1], 2), round(worldPos[2], 2)] + + for each in influences: + infPos = cmds.xform(each, q=True, ws=True, t=True) + newPos = [round(infPos[0], 2), round(infPos[1], 2), round(infPos[2], 2)] + if newPos == mirrorPos: + mirrorInfs.append(each) + + # now we have all of the data needed to perform the mirror + data = [vert, mirrorVertex, vertexInfs, mirrorInfs, infValues] + + # perform mirror + vert = data[1] + infs = data[3] + vals = data[4] + + # transformValueList + if vert != None: + if len(data[2]) == len(data[3]): + cmds.skinPercent(skinCluster, vert, transformValue=[infs[0], 1.0]) + + transformValueList = [] + for i in range(len(infs)): + transformValueList.append((str(infs[i]), float(vals[i]))) + + cmds.skinPercent(skinCluster, vert, transformValue=transformValueList) + + else: + cmds.warning("skipping " + str(data[1]) + ". Missing symmetrical influence.") + + if len(mirrorVertices) > 0: + cmds.select(mirrorVertices) + else: + cmds.select(selection) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_copySkinWeights(self): + + selection = cmds.ls(sl=True) + + if len(selection) > 1: + fromObj = selection[0] + + fromSkinCluster = riggingUtils.findRelatedSkinCluster(fromObj) + fromJoints = cmds.skinCluster(fromSkinCluster, q=True, inf=True) + + cmds.select(selection[1]) + cmds.select(fromJoints, tgl=True) + + mel.eval("doEnableNodeItems false all;") + cmds.dagPose(r=True, g=True, bp=True) + + newSkinCluster = cmds.skinCluster(tsb=True, mi=0, dr=4, sm=0)[0] + cmds.copySkinWeights(ss=fromSkinCluster, ds=newSkinCluster, noMirror=True) + mel.eval("doEnableNodeItems true all;") + else: + cmds.warning("Please select a source and target mesh.") + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_hammerSkinWeights(self): + + try: + mel.eval("weightHammerVerts;") + + except: + cmds.confirmDialog(icon="warning", title="Hammer Skin Weights", + message="The weight hammer works on polygon vertices. Please select at least 1 vertex.") + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_growOrShrink(self, mode): + + # validate selection + valid = False + selection = cmds.ls(sl=True, flatten=True) + for each in selection: + if each.find("vtx") != -1: + valid = True + + if valid: + cmds.polySelectConstraint(t=0x0001, pp=mode) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_loopOrRing(self, mode): + + # check length of selection + valid = False + selection = cmds.ls(sl=True, flatten=True) + for each in selection: + if each.find("vtx") != -1: + valid = True + + # check length of selection + if valid: + if len(selection) >= 2: + # convert the selection to edges + edges = cmds.polyListComponentConversion(fv=True, te=True, internal=True) + cmds.select(edges) + + # get the edge loop or ring + if mode == "loop": + cmds.polySelectSp(loop=True) + if mode == "ring": + cmds.polySelectSp(ring=True) + + # set back to verts + verts = cmds.polyListComponentConversion(fe=True, tv=True) + cmds.select(verts) + else: + cmds.warning("Please select at least two vertices.") + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_shell(self): + + # validate selection + valid = False + selection = cmds.ls(sl=True, flatten=True) + for each in selection: + if each.find("vtx") != -1: + valid = True + if valid: + cmds.ConvertSelectionToShell() + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_isolate(self): + + # get button state + state = self.isolateSelection.isChecked() + + # validate selection + valid = False + selection = cmds.ls(sl=True, flatten=True) + for each in selection: + if each.find("vtx") != -1: + valid = True + + if valid: + if state: + # convert selection to faces, grab shell, then back to verts + cmds.select(cmds.polyListComponentConversion(tf=True)) + cmds.ConvertSelectionToShell() + + # isolate selection + isoPnl = cmds.getPanel(wf=True) + isoCrnt = cmds.isolateSelect(isoPnl, q=True, s=True) + mel.eval('enableIsolateSelect %s %d' % (isoPnl, not isoCrnt)) + + # convert back to verts + cmds.select(cmds.polyListComponentConversion(tv=True)) + + # change the button text to exit isolation mode + self.isolateSelection.setText("Exit Isolation Mode") + + if not state: + isoPnl = cmds.getPanel(wf=True) + isoCrnt = cmds.isolateSelect(isoPnl, q=True, s=True) + mel.eval('enableIsolateSelect %s %d' % (isoPnl, not isoCrnt)) + + # change the button text to exit isolation mode + self.isolateSelection.setText("Isolate Selection") + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_addWeight(self, amount): + + # if no amount passed in, get amount from setWeight spin box + if amount == None: + amount = self.customWeightField.value() + + # get the selected transform from either list + selectedTransformData = self.weightTable_getSelectedTransform() + selectedTransform = selectedTransformData[0] + lockInfs = selectedTransformData[1] + length = selectedTransformData[2] + + # get skin cluster for selected verts + cluster = self.weightTable_getSkinForSelected(selectedTransform, lockInfs) + + # skin the selected verts to the selected trasnform with the incoming amount + canProceed = self.weightTable_checkIfLocked() + if canProceed: + if selectedTransform != None: + if length > 2: + cmds.undoInfo(openChunk=True) + cmds.skinPercent(cluster, transformValue=[selectedTransform, amount]) + cmds.undoInfo(closeChunk=True) + if selectedTransform != None: + if length == 2: + if len(self.weightTable_skinJoints.selectedItems()) > 0: + cmds.undoInfo(openChunk=True) + cmds.skinPercent(cluster, transformValue=[selectedTransform, amount]) + cmds.undoInfo(closeChunk=True) + + if selectedTransform == None: + cmds.warning("No influence selected in list to add weight to.") + + if lockInfs: + self.weightTable_lockInfs(selectedTransform, lockInfs, cluster, False) + + # refresh UI + self.weightTable_getInfs() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_getSelectedTransform(self): + + # get the selected transform from either list + selectedTransform = None + lockInfs = False + length = self.weightTableVertList.count() + + if len(self.weightTable_skinJoints.selectedItems()) > 0: + selectedTransformItem = self.weightTable_skinJoints.selectedItems() + selectedTransform = selectedTransformItem[0].text() + + if len(self.weightTableVertList.selectedItems()) > 0: + selectedTransformItem = self.weightTableVertList.selectedItems() + selectedTransform = selectedTransformItem[0].text() + lockInfs = True + + return [selectedTransform, lockInfs, length] + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_getSkinForSelected(self, selectedTransform, lockInfs): + # if vertices are selected, get that selection + if self.weightTableVertList.count() > 0: + selection = cmds.ls(sl=True, fl=True) + # find the shape node associated with the seleted vertices + shape = cmds.listRelatives(cmds.ls(sl=True), parent=True)[0] + # find the transform of the shape + transform = cmds.listRelatives(shape, parent=True)[0] + # find the skin cluster associated with the shape node + connnections = cmds.listConnections(shape, connections=True) + for c in connnections: + if cmds.nodeType(c) == "skinCluster": + cluster = c + if selectedTransform != None: + # now if lockInfs is True, lock all influences not in the weightTableVertList + if lockInfs: + self.weightTable_lockInfs(selectedTransform, lockInfs, c, True) + return cluster + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_checkIfLocked(self): + + numLocked = [] + for i in range(self.weightTableVertList.count()): + item = self.weightTableVertList.item(i) + jointName = item.text() + + # check to see if the joint is locked + if cmds.objExists(jointName): + locked = cmds.skinCluster(self.skinCluster, q=True, inf=jointName, lw=True) + if locked: + numLocked.append(jointName) + + if len(numLocked) < (self.weightTableVertList.count() - 1): + return True + else: + return False + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_lockInfs(self, selectedTransform, lockInfs, cluster, lock): + # get all of the influences + allInfs = [] + previouslyLocked = [] + allInfItems = self.weightTable_skinJoints.count() + for i in range(allInfItems): + allInfs.append(self.weightTable_skinJoints.item(i).text()) + + # get the influences in the weightTableVertList + doNotLock = [] + vertsInfs = self.weightTableVertList.count() + for i in range(vertsInfs): + jointName = self.weightTableVertList.item(i).text() + if cmds.objExists(jointName): + locked = cmds.skinCluster(cluster, q=True, inf=jointName, lw=True) + if not locked: + doNotLock.append(self.weightTableVertList.item(i).text()) + if locked: + previouslyLocked.append(self.weightTableVertList.item(i).text()) + + # lock infs not in doNotLock list + if lock: + for inf in allInfs: + if inf not in doNotLock: + cmds.skinCluster(cluster, edit=True, inf=inf, lockWeights=True) + + if lock == False: + # unlock infs not in doNotLock list + for inf in allInfs: + if inf not in doNotLock: + cmds.skinCluster(cluster, edit=True, inf=inf, lockWeights=False) + for inf in previouslyLocked: + cmds.skinCluster(cluster, edit=True, inf=inf, lockWeights=True) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_getInfs(self): + + currentSelection = None + listWidget = None + # get current selection in lists + if len(self.weightTable_skinJoints.selectedItems()) > 0: + currentSelection = self.weightTable_skinJoints.selectedItems()[0].text() + listWidget = self.weightTable_skinJoints + + if len(self.weightTableVertList.selectedItems()) > 0: + currentSelection = self.weightTableVertList.selectedItems()[0].text() + listWidget = self.weightTableVertList + + # clear the influence list + self.weightTable_skinJoints.clear() + self.weightTableVertList.clear() + self.weightTableInfList.clear() + + # find the selected mesh + selection = cmds.ls(sl=True) + if len(selection) >= 1: + mesh = selection[0] + + if cmds.nodeType(mesh) == "transform": + self.weightTable_populateSkinJoints(mesh) + if cmds.nodeType(mesh) == "mesh": + shapeNode = cmds.listRelatives(mesh, parent=True) + if shapeNode != None: + transformNode = cmds.listRelatives(shapeNode[0], parent=True) + if transformNode != None: + cluster = self.weightTable_populateSkinJoints(transformNode[0]) + + # vertex influence list + self.weightTable_populateVertexJoints(selection, cluster) + + # reselect + if currentSelection != None: + items = listWidget.findItems(currentSelection, QtCore.Qt.MatchExactly) + if len(items) == 1: + row = listWidget.row(items[0]) + listWidget.setCurrentRow(row) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_populateSkinJoints(self, mesh): + + # check for skinCluster + skinClusters = cmds.ls(type='skinCluster') + self.influences = [] + + # go through each found skin cluster, and if we find a skin cluster whose geometry matches our selection, get influences + for cluster in skinClusters: + geometry = cmds.skinCluster(cluster, q=True, g=True)[0] + geoTransform = cmds.listRelatives(geometry, parent=True)[0] + if geoTransform == mesh: + self.skinCluster = cluster + self.influences = cmds.skinCluster(cluster, q=True, inf=True) + + # add the influneces to the list + if len(self.influences) > 0: + for inf in self.influences: + self.weightTable_skinJoints.addItem(inf) + + # return info + if self.skinCluster != None: + return self.skinCluster + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_populateVertexJoints(self, vertices, cluster): + + # find all of the influences of the selected vertices + influenceJoints = [] + for vertex in vertices: + infs = cmds.skinPercent(cluster, vertex, q=True, transform=None, ib=.001) + for inf in infs: + influenceJoints.append(inf) + + # remove duplicates from the influence joints list + vertInfluences = [] + vertInfluences = list(set(influenceJoints)) + + # re-create the headers + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + customColor = QtGui.QColor(25, 175, 255) + labelItem = QtWidgets.QListWidgetItem("Joint") + labelItem.setFont(headerFont) + labelItem.setFlags(QtCore.Qt.NoItemFlags) + labelItem.setForeground(customColor) + labelItem.setBackground(QtCore.Qt.black) + labelItem.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.weightTableVertList.addItem(labelItem) + + # add these to the weightTableVertList + lockColor = QtGui.QColor(255, 0, 0) + lockFont = QtGui.QFont() + lockFont.setPointSize(8) + lockFont.setBold(True) + + for each in vertInfluences: + + item = QtWidgets.QListWidgetItem(each) + locked = cmds.skinCluster(cluster, q=True, inf=each, lw=True) + if locked: + item.setFont(lockFont) + item.setForeground(lockColor) + + self.weightTableVertList.addItem(item) + + # find average weights + self.weightTable_populateAvgWeight(vertices, cluster, vertInfluences) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_populateAvgWeight(self, vertices, cluster, influences): + + # find the infleunce values for each vertex + averages = [] + for influence in influences: + data = [] + for vert in vertices: + value = cmds.skinPercent(cluster, vert, q=True, transform=influence) + data.append(value) + avg = sum(data) / float(len(data)) + averages.append(avg) + + # recreate the header + customColor = QtGui.QColor(25, 175, 255) + headerFont = QtGui.QFont() + headerFont.setPointSize(8) + headerFont.setBold(True) + + labelValue = QtWidgets.QListWidgetItem("Avg. Weight") + labelValue.setFont(headerFont) + labelValue.setFlags(QtCore.Qt.NoItemFlags) + labelValue.setForeground(customColor) + labelValue.setBackground(QtCore.Qt.black) + labelValue.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.weightTableInfList.addItem(labelValue) + + # add averages + for each in averages: + entry = '{0:.10f}'.format(each) + self.weightTableInfList.addItem(str(entry)) + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_inrementWeight(self, direction): + + # get the amount from setWeight + amount = self.customWeightField.value() + + # get the selected transform from either list + selectedTransformData = self.weightTable_getSelectedTransform() + selectedTransform = selectedTransformData[0] + lockInfs = selectedTransformData[1] + length = selectedTransformData[2] + + # get skin cluster for selected verts + cluster = self.weightTable_getSkinForSelected(selectedTransform, lockInfs) + + # skin the selected verts to the selected trasnform with the incoming amount + if length > 1: + if selectedTransform != None: + if direction == "up": + cmds.undoInfo(openChunk=True) + cmds.skinPercent(cluster, relative=True, transformValue=[selectedTransform, amount]) + cmds.undoInfo(closeChunk=True) + + if direction == "down": + cmds.undoInfo(openChunk=True) + cmds.skinPercent(cluster, relative=True, transformValue=[selectedTransform, (amount * -1)]) + cmds.undoInfo(closeChunk=True) + + if lockInfs: + self.weightTable_lockInfs(selectedTransform, lockInfs, cluster, False) + + # refresh UI + self.weightTable_getInfs() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_scaleWeight(self, direction): + + # if no amount passed in, get amount from setWeight spin box + amount = self.scaleCustomWeightField.value() + + # get the selected transform from either list + selectedTransformData = self.weightTable_getSelectedTransform() + selectedTransform = selectedTransformData[0] + lockInfs = selectedTransformData[1] + length = selectedTransformData[2] + + # get skin cluster for selected verts + cluster = self.weightTable_getSkinForSelected(selectedTransform, lockInfs) + + # skin the selected verts to the selected trasnform with the incoming amount + selection = cmds.ls(sl=True, fl=True) + for each in selection: + currentValue = cmds.skinPercent(cluster, each, transform=selectedTransform, q=True) + if length > 2: + if selectedTransform != None: + if direction == None: + cmds.undoInfo(openChunk=True) + cmds.skinPercent(cluster, transformValue=[selectedTransform, (currentValue * amount)]) + cmds.undoInfo(closeChunk=True) + + if direction == "up": + cmds.undoInfo(openChunk=True) + cmds.skinPercent(cluster, transformValue=[selectedTransform, + (((currentValue * 5) / 100) + currentValue)]) + cmds.undoInfo(closeChunk=True) + + if direction == "down": + cmds.undoInfo(openChunk=True) + cmds.skinPercent(cluster, transformValue=[selectedTransform, + (currentValue - ((currentValue * 5) / 100))]) + cmds.undoInfo(closeChunk=True) + + if lockInfs: + self.weightTable_lockInfs(selectedTransform, lockInfs, cluster, False) + + # refresh UI + self.weightTable_getInfs() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_copyWeight(self): + + self.weightCopy_infList = [] + self.weightCopy_valList = [] + + selection = cmds.ls(sl=True, flatten=True) + if len(selection) > 1: + cmds.warning("Please select only 1 vertex.") + return + + else: + if selection[0].find(".vtx") != -1: + # get weight values + self.weightCopy_infList = cmds.skinPercent(self.skinCluster, selection[0], q=True, transform=None, + ib=.001) + self.weightCopy_valList = cmds.skinPercent(self.skinCluster, selection[0], q=True, value=True, ib=.001) + + # update line edit + self.vertexBufferLayout.setText(selection[0] + " copied to buffer") + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_pasteWeight(self): + + proceed = False + + # get selected vertices and determine if selection is valid + selection = cmds.ls(sl=True, flatten=True) + for each in selection: + if each.find(".vtx") != -1: + proceed = True + + # if selection is valid, paste the weights from the buffer + if proceed: + for each in selection: + transformPairs = [] + try: + if len(self.weightCopy_infList) > 0: + for i in range(len(self.weightCopy_infList)): + transformPairs.append((self.weightCopy_infList[i], self.weightCopy_valList[i])) + + cmds.undoInfo(openChunk=True) + cmds.skinPercent(self.skinCluster, each, transformValue=transformPairs) + cmds.undoInfo(closeChunk=True) + + except: + pass + + self.weightTable_getInfs() + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_blendWeight(self): + + proceed = False + + # get selected vertices and determine if selection is valid + selection = cmds.ls(sl=True, flatten=True) + for each in selection: + if each.find(".vtx") != -1: + proceed = True + + # if selection is valid, grow the selection + cmds.polySelectConstraint(t=0x0001, pp=1) + cmds.polySelectConstraint(t=0x0001, pp=1) + newSelection = cmds.ls(sl=True, flatten=True) + + # filter out original selection from new selection + for vert in newSelection: + if vert in selection: + cmds.select(vert, tgl=True) + + vertsToBlend = cmds.ls(sl=True, flatten=True) + + # weight hammer + mel.eval("weightHammerVerts;") + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_connectInfluenceLists(self, topLevel, vertLevel, *args): + + # connect the skin joints list the the vertex skin info joint list so that only 1 item can be selected in either list at a time + if topLevel: + if len(self.weightTableVertList.selectedItems()) > 0: + self.weightTableVertList.clearSelection() + if vertLevel: + if len(self.weightTable_skinJoints.selectedItems()) > 0: + self.weightTable_skinJoints.clearSelection() + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_showInfluencedVerts(self, inSelection): + + # store current selection + currentSelection = cmds.ls(sl=True, fl=True) + + # get selected joint in influence list + selected = self.weightTable_skinJoints.selectedItems() + if len(selected) > 1: + cmds.warning("Please select only 1 joint in the list.") + if len(selected) == 1: + joint = selected[0].text() + cmds.selectMode(component=True) + cmds.skinCluster(self.skinCluster, edit=True, siv=joint) + newSelection = cmds.ls(sl=True, fl=True) + + # if the new selection is nothing new, select the original selection and put the mesh in the correct selectMode + if len(newSelection) == 1: + if cmds.nodeType(newSelection[0]) == "mesh": + if len(currentSelection) == 1: + cmds.selectMode(object=True) + cmds.select(currentSelection) + cmds.warning("No influenced vertices for that joint.") + + # if inSelection arg is True, compare the original selection to the new selection + if inSelection: + selectSet = [] + for each in newSelection: + if each in currentSelection: + selectSet.append(each) + + # select new selectSet + cmds.select(selectSet) + + # refresh UI + self.weightTable_getInfs() + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def weightTable_focusInfluencedVerts(self): + + # store current selection + currentSelection = cmds.ls(sl=True, fl=True) + + # get selected joint in influence list + selected = self.psw_influenceList.selectedItems() + if len(selected) > 1: + cmds.warning("Please select only 1 joint in the list.") + if len(selected) == 1: + joint = selected[0].text() + cmds.selectMode(component=True) + cmds.skinCluster(self.skinCluster, edit=True, siv=joint) + + # fit and view + utils.fitSelection() + + # select original selection + cmds.select(currentSelection) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_focusInfluencedJoint(self): + + # store current selection + currentSelection = cmds.ls(sl=True, fl=True) + + # get selected joint in influence list + selected = self.psw_influenceList.selectedItems() + if len(selected) > 1: + cmds.warning("Please select only 1 joint in the list.") + if len(selected) == 1: + joint = selected[0].text() + cmds.select(joint) + + # fit and view + utils.fitSelection() + + # select original selection + cmds.select(currentSelection) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def weightTable_toggleJointSelectMode(self): + + selectable = self.weightTableJointSel.isChecked() + + if selectable: + cmds.selectType(joint=False) + else: + cmds.selectType(joint=True) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def createContextMenuWeightTable(self, point): + + contextMenu = QtWidgets.QMenu() + contextMenu.addAction("Unlock Joint", self.contextMenu_unlockJoint) + contextMenu.addAction("Lock Joint", self.contextMenu_lockJoint) + contextMenu.exec_(self.weightTableVertList.mapToGlobal(point)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def contextMenu_unlockJoint(self): + + try: + selected = self.weightTableVertList.selectedItems() + selected = selected[0].text() + cmds.skinCluster(self.skinCluster, edit=True, inf=selected, lw=False) + self.weightTable_getInfs() + except: + pass + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def contextMenu_lockJoint(self): + + try: + selected = self.weightTableVertList.selectedItems() + selected = selected[0].text() + cmds.skinCluster(self.skinCluster, edit=True, inf=selected, lw=True) + self.weightTable_getInfs() + except: + pass + + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_searchModules(self): + searchKey = self.psw_modSearchBar.text() + searchKeys = [] + + if searchKey.find(",") != -1: + searchKeys = searchKey.split(",") + else: + searchKeys.append(searchKey) + + # get all items in the joint list + allItems = [] + for i in range(self.psw_moduleInfluenceList.count()): + item = self.psw_moduleInfluenceList.item(i) + itemName = item.text() + allItems.append([item, itemName]) + + # hide all items in list + for item in allItems: + item[0].setHidden(True) + item[0].setSelected(False) + + # find items in list with search key and show item + if searchKey.find("*") == 0: + matchedItems = self.psw_moduleInfluenceList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchWildcard) + for item in matchedItems: + item.setHidden(False) + item.setSelected(True) + + else: + matchedItems = [] + for searchKey in searchKeys: + matchedItems.extend( + self.psw_moduleInfluenceList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchContains)) + + for item in matchedItems: + item.setHidden(False) + item.setSelected(True) + + # populate infs + self.paintSkinWeights_populateInfs() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeights_searchInfluences(self): + + searchKey = self.psw_infSearchBar.text() + # get all items in the joint list + allItems = [] + for i in range(self.psw_influenceList.count()): + item = self.psw_influenceList.item(i) + itemName = item.text() + allItems.append([item, itemName]) + + # hide all items in list + for item in allItems: + item[0].setHidden(True) + item[0].setSelected(False) + + # find items in list with search key and show item + if searchKey.find("*") == 0: + matchedItems = self.psw_influenceList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchWildcard) + for item in matchedItems: + item.setHidden(False) + item.setSelected(True) + + else: + matchedItems = self.psw_influenceList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchContains) + for item in matchedItems: + item.setHidden(False) + item.setSelected(True) + print "selecting" + str(item.text()) + self.paintSkinWeights_changeInf() + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_createContextMenu(self, point): + + contextMenu = QtWidgets.QMenu() + contextMenu.addAction("Unlock Selected Joint", partial(self.paintSkinWeights_lockJoint, False, True)) + contextMenu.addAction("Unlock Unselected Joints", partial(self.paintSkinWeights_lockJoint, False, False)) + contextMenu.addAction("Lock Selected Joint", partial(self.paintSkinWeights_lockJoint, True, True)) + contextMenu.addAction("Lock Unselected Joints", partial(self.paintSkinWeights_lockJoint, True, False)) + + contextMenu.addAction("Focus Vertices Influenced by Joint", self.weightTable_focusInfluencedVerts) + contextMenu.addAction("Focus Selected Joint", self.weightTable_focusInfluencedJoint) + + contextMenu.exec_(self.psw_influenceList.mapToGlobal(point)) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeights_lockJoint(self, lock, selected): + + try: + selectedItem = self.psw_influenceList.selectedItems() + selectedItem = selectedItem[0].text() + + if selected: + cmds.skinCluster(self.skinCluster, edit=True, inf=selectedItem, lw=lock) + + if not selected: + for i in range(self.psw_influenceList.count()): + item = self.psw_influenceList.item(i).text() + if item != selectedItem: + cmds.skinCluster(self.skinCluster, edit=True, inf=item, lw=lock) + + self.paintSkinWeights_populateInfs() + except: + pass + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_updateCTX(self, attribute, value, widget, *args): + + # special cases + if value == None: + value = str(widget.isChecked()) + + if value == "mapping": + index = self.psw_stylusPressureOptions.currentIndex() + if index == 0: + value = str("\"" + "Opacity" + "\"") + if index == 1: + value = str("\"" + "Radius" + "\"") + if index == 2: + value = str("\"" + "Both" + "\"") + + if value == "paintOp": + index = self.psw_paintOperation.currentIndex() + if index == 0: + value = str("\"" + "additive" + "\"") + if index == 1: + value = str("\"" + "absolute" + "\"") + if index == 2: + value = str("\"" + "scale" + "\"") + if index == 3: + value = str("\"" + "smooth" + "\"") + + if value == "slider": + value = str(widget.value()) + + if value == "sliderEdit": + value = str(float(widget.value()) / 100) + currentCtx = cmds.currentCtx() + execString = "cmds.artAttrSkinPaintCtx(" + "\"" + currentCtx + "\"" + ", edit = True, " + attribute + " = " + value + ")" + try: + exec execString + except Exception as e: + print e + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_togglePaintProfile(self, button, img): + + for each in self.paintProfileButtons: + widget = each[0] + image = each[1] + + icon = QtGui.QIcon(image) + widget.setIcon(icon) + + state = button.isChecked() + + if state: + icon = QtGui.QIcon(image) + widget.setIcon(icon) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_UpdateSliders(self, getValueFrom, putValueOn, doMath, *args): + + # if getting the value from the slider, divide by 100 when setting doubleSpinBox + value = getValueFrom.value() + + if doMath: + if value > 1: + value = float(value) / 100 + putValueOn.setValue(value) + if value == 0: + putValueOn.setValue(float(0.00)) + + if not doMath: + putValueOn.setValue(value) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_SetSliderValues(self, slider, value, *args): + + slider.setValue(value) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeights_floodWeights(self): + + value = self.psw_valueSlider.value() + value = value / 100 + + currentCtx = cmds.currentCtx() + currentMode = cmds.artAttrSkinPaintCtx(currentCtx, q=True, sao=True) + if currentMode == "additive": + cmds.artAttrSkinPaintCtx(currentCtx, edit=True, clear=True) + else: + cmds.artAttrSkinPaintCtx(currentCtx, edit=True, clear=value) + + + ############################################################################################################### + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # EXTERNAL FILES CALLED + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ############################################################################################################### + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + def paintSkinWeights_deformationWizardLaunch(self): + + import ART_WeightWizard as aww + reload(aww) + aww.run(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeights_smoothSkinUI(self): + + import ART_WeightWizard as aww + reload(aww) + aww.runOnlySkinTools(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeights_importSkinWeights(self): + + import ART_ImportWeights + reload(ART_ImportWeights) + ART_ImportWeights.ART_ImportSkinWeights(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def paintSkinWeights_exportSkinWeights(self): + + import ART_ExportWeights + reload(ART_ExportWeights) + ART_ExportWeights.ART_ExportSkinWeights(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addOrRemoveInfs_UI(self): + + import ART_AddOrRemoveInfluences + reload(ART_AddOrRemoveInfluences) + ART_AddOrRemoveInfluences.ART_AddOrRemoveInfluences(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def moveInfluences_UI(self): + + import ART_MoveInfluences + reload(ART_MoveInfluences) + ART_MoveInfluences.ART_MoveInfluences(self) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def debugRigs(self): + # Original Author: Jeremy Ernst + + # run publish process + import ART_DebugRigs + reload(ART_DebugRigs) + ART_DebugRigs.ART_DebugRigs(self.mainUI) diff --git a/Core/Scripts/Interfaces/ART_SymmetryModeUI.py b/Core/Scripts/Interfaces/ART_SymmetryModeUI.py new file mode 100644 index 0000000..5ff601b --- /dev/null +++ b/Core/Scripts/Interfaces/ART_SymmetryModeUI.py @@ -0,0 +1,312 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import System.utils as utils + + + +class ART_SymmetryMode(): + + def __init__(self, mainUI): + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + #build the UI + self.buildSymmetryModeUI(mainUI) + self.mainUI = mainUI + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def buildSymmetryModeUI(self, mainUI): + + if cmds.window("ART_SymmetryModeWin", exists = True): + cmds.deleteUI("ART_SymmetryModeWin", wnd = True) + + #launch a UI to get the name information + self.symmetryModeWin = QtWidgets.QMainWindow(mainUI) + + #load stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + style = f.read() + f.close() + + self.symmetryModeWin.setStyleSheet(style) + + + #size policies + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.symModeWin_mainWidget = QtWidgets.QWidget() + self.symmetryModeWin.setCentralWidget(self.symModeWin_mainWidget) + + #set qt object name + self.symmetryModeWin.setObjectName("ART_SymmetryModeWin") + self.symmetryModeWin.setWindowTitle("Mass Mirror Mode") + + #create the mainLayout for the rig creator UI + self.symModeWin_mainLayout = QtWidgets.QVBoxLayout(self.symModeWin_mainWidget) + self.symModeWin_mainLayout.setContentsMargins(0, 0, 0, 0) + + self.symmetryModeWin.setSizePolicy(mainSizePolicy) + self.symmetryModeWin.setMinimumSize(QtCore.QSize( 600, 250 )) + self.symmetryModeWin.setMaximumSize(QtCore.QSize( 600, 250 )) + + #create the background + self.symModeWin_frame = QtWidgets.QFrame() + self.symModeWin_frame.setObjectName("mid") + self.symModeWin_mainLayout.addWidget(self.symModeWin_frame) + + + + #create the layout for the widgets + self.symModeWin_widgetLayout = QtWidgets.QHBoxLayout(self.symModeWin_frame) + self.symModeWin_widgetLayout.setContentsMargins(5, 5, 5, 5) + + #add the QListWidget Frame + self.symModeWin_moduleListFrame = QtWidgets.QFrame() + self.symModeWin_moduleListFrame.setObjectName("mid") + self.symModeWin_moduleListFrame.setMinimumSize(QtCore.QSize( 450, 200 )) + self.symModeWin_moduleListFrame.setMaximumSize(QtCore.QSize( 450, 200 )) + self.symModeWin_moduleListFrame.setContentsMargins(20,0,20,0) + + + #create the list widget + self.symModeWin_moduleList = QtWidgets.QListWidget(self.symModeWin_moduleListFrame) + self.symModeWin_widgetLayout.addWidget(self.symModeWin_moduleListFrame) + self.symModeWin_moduleList.setMinimumSize(QtCore.QSize( 450, 200 )) + self.symModeWin_moduleList.setMaximumSize(QtCore.QSize( 450, 200 )) + self.symModeWin_moduleList.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) + self.symModeWin_moduleList.setSpacing(3) + + + #add the layout for the buttons + self.symModeWin_buttonLayoutAll = QtWidgets.QVBoxLayout() + self.symModeWin_widgetLayout.addLayout(self.symModeWin_buttonLayoutAll) + self.symModeWin_buttonLayoutAll.setContentsMargins(5, 20, 5, 20) + + #add the selection buttons + self.symModeWin_selectionButtonLayout = QtWidgets.QVBoxLayout() + self.symModeWin_buttonLayoutAll.addLayout(self.symModeWin_selectionButtonLayout) + self.symModeWin_selectAllButton = QtWidgets.QPushButton("Select All") + self.symModeWin_selectAllButton.setMinimumSize(QtCore.QSize( 115, 25 )) + self.symModeWin_selectAllButton.setMaximumSize(QtCore.QSize( 115, 25 )) + self.symModeWin_selectionButtonLayout.addWidget(self.symModeWin_selectAllButton) + self.symModeWin_selectAllButton.clicked.connect(partial(self.symmetryMode_selectDeselect, True)) + self.symModeWin_selectAllButton.setObjectName("blueButton") + + self.symModeWin_selectNoneButton = QtWidgets.QPushButton("Clear Selection") + self.symModeWin_selectNoneButton.setMinimumSize(QtCore.QSize( 115, 25 )) + self.symModeWin_selectNoneButton.setMaximumSize(QtCore.QSize( 115, 25 )) + self.symModeWin_selectionButtonLayout.addWidget(self.symModeWin_selectNoneButton) + self.symModeWin_selectNoneButton.clicked.connect(partial(self.symmetryMode_selectDeselect, False)) + self.symModeWin_selectNoneButton.setObjectName("blueButton") + + #spacer + spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.symModeWin_buttonLayoutAll.addItem(spacerItem) + + #add the mirror buttons + self.symModeWin_mirrorButtonLayout = QtWidgets.QVBoxLayout() + self.symModeWin_buttonLayoutAll.addLayout(self.symModeWin_mirrorButtonLayout) + self.symModeWin_mirrorL2RButton = QtWidgets.QPushButton("Mirror Checked") + self.symModeWin_mirrorL2RButton.setToolTip("Mirror selected modules to unselected modules") + + self.symModeWin_mirrorL2RButton.setMinimumSize(QtCore.QSize( 115, 25 )) + self.symModeWin_mirrorL2RButton.setMaximumSize(QtCore.QSize( 115, 25 )) + self.symModeWin_mirrorButtonLayout.addWidget(self.symModeWin_mirrorL2RButton) + self.symModeWin_mirrorL2RButton.setObjectName("blueButton") + + self.symModeWin_mirrorL2RButton.clicked.connect(partial(self.symmetryMode_mirror)) + + + + #populate the list widget + modules = utils.returnRigModules() + entries = [] + listMods = [] + + for mod in modules: + modName = cmds.getAttr(mod + ".moduleName") + mirrorModule = cmds.getAttr(mod + ".mirrorModule") + invalidTypes = [None, "None"] + if mirrorModule not in invalidTypes: + if modName not in listMods: + entries.append([modName, mirrorModule]) + listMods.append(modName) + listMods.append(mirrorModule) + + self.symModeWinModues = {} + + if len(entries) == 0: + item = QtWidgets.QListWidgetItem(self.symModeWin_moduleList) + label = QtWidgets.QLabel("No modules with mirroring setup") + item.setSizeHint(label.sizeHint()) + self.symModeWin_moduleList.addItem(item) + self.symModeWin_moduleList.setItemWidget(item, label) + + + for each in entries: + + #create a custom widget to add to each entry in the listWidget + mainWidget = QtWidgets.QWidget() + buttonLayout = QtWidgets.QHBoxLayout(mainWidget) + + #create the checkbox + checkbox = QtWidgets.QCheckBox() + checkbox.setMinimumSize(QtCore.QSize( 12, 12 )) + checkbox.setMaximumSize(QtCore.QSize( 12, 12 )) + checkbox.setChecked(True) + buttonLayout.addWidget(checkbox) + + label = QtWidgets.QLabel("Mirror ") + buttonLayout.addWidget(label) + + mirrorFrom = QtWidgets.QComboBox() + mirrorFrom.addItem(each[0]) + mirrorFrom.addItem(each[1]) + buttonLayout.addWidget(mirrorFrom) + mirrorFrom.setMinimumWidth(150) + + + label = QtWidgets.QLabel(" To ") + buttonLayout.addWidget(label) + label.setAlignment(QtCore.Qt.AlignCenter) + + mirrorTo = QtWidgets.QComboBox() + mirrorTo.addItem(each[1]) + mirrorTo.addItem(each[0]) + buttonLayout.addWidget(mirrorTo) + mirrorTo.setMinimumWidth(150) + + #signal/slots + mirrorFrom.currentIndexChanged.connect(partial(self.toggleComboBoxFrom, mirrorFrom, mirrorTo)) + mirrorTo.currentIndexChanged.connect(partial(self.toggleComboBoxTo, mirrorFrom, mirrorTo)) + + #add this item widget to the list + item = QtWidgets.QListWidgetItem(self.symModeWin_moduleList) + index = entries.index(each) + if (index % 2) == 0: + item.setBackground(QtGui.QColor(106,106,108)) + else: + item.setBackground(QtGui.QColor(46,46,48)) + + item.setSizeHint(mainWidget.sizeHint()) + self.symModeWin_moduleList.addItem(item) + self.symModeWin_moduleList.setItemWidget(item, mainWidget) + + + + #show the window + self.symmetryModeWin.show() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def symmetryMode_selectDeselect(self, state): + + + #find all items in the list + items = self.symModeWin_moduleList.findItems('', QtCore.Qt.MatchRegExp) + for each in items: + itemWidget = self.symModeWin_moduleList.itemWidget(each) + + try: + #get the layout of this widget + layout = itemWidget.findChild(QtWidgets.QHBoxLayout) + #find the checbox and check it + for i in range(layout.count()): + item = layout.itemAt(i) + if type(item.widget()) == QtWidgets.QCheckBox: + item.widget().setChecked(state) + except: + pass + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleComboBoxFrom(self, mirrorFrom, mirrorTo, *args): + + if mirrorFrom.currentText() == mirrorTo.currentText(): + index = mirrorTo.findText(mirrorFrom.currentText(), QtCore.Qt.MatchExactly) + if index == 0: + mirrorTo.setCurrentIndex(1) + else: + mirrorTo.setCurrentIndex(0) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleComboBoxTo(self, mirrorFrom, mirrorTo, *args): + + if mirrorTo.currentText() == mirrorFrom.currentText(): + index = mirrorFrom.findText(mirrorTo.currentText(), QtCore.Qt.MatchExactly) + if index == 0: + mirrorFrom.setCurrentIndex(1) + else: + mirrorFrom.setCurrentIndex(0) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def symmetryMode_mirror(self): + + #find all items in the list + items = self.symModeWin_moduleList.findItems('', QtCore.Qt.MatchRegExp) + for each in items: + itemWidget = self.symModeWin_moduleList.itemWidget(each) + + try: + #get the layout of this widget + layout = itemWidget.findChild(QtWidgets.QHBoxLayout) + + #find the checkbox + state = False + for i in range(layout.count()): + item = layout.itemAt(i) + + if type(item.widget()) == QtWidgets.QCheckBox: + #get the checkbox state + state = item.widget().isChecked() + + if type(item.widget()) == QtWidgets.QComboBox: + if state == True: + moduleName = item.widget().currentText() + modules = utils.returnRigModules() + for mod in modules: + modName = cmds.getAttr(mod + ".moduleName") + + if modName == moduleName: + modType = cmds.getAttr(mod + ".moduleType") + importMod = __import__("RigModules." + modType, {}, {}, [modType]) + reload(importMod) + + moduleClass = getattr(importMod, importMod.className) + moduleInst = moduleClass(self.mainUI, modName) + moduleInst.mirrorTransformations() + + except: + pass
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/ART_WeightWizard.py b/Core/Scripts/Interfaces/ART_WeightWizard.py new file mode 100644 index 0000000..fd349e4 --- /dev/null +++ b/Core/Scripts/Interfaces/ART_WeightWizard.py @@ -0,0 +1,773 @@ +from ThirdParty.Qt import QtGui, QtCore, QtWidgets +from functools import partial +import maya.cmds as cmds +import os +import System.utils as utils +import System.riggingUtils as riggingUtils + +#maya 2016< maya2017> compatability +try: + import shiboken as shiboken +except: + import shiboken2 as shiboken + + + +def getMainWindow(): + import maya.OpenMayaUI as mui + pointer = mui.MQtUtil.mainWindow() + #pyside QMainWindow takes in a QWidget rather than QObject + return shiboken.wrapInstance(long(pointer), QtWidgets.QWidget) + + +windowTitle = "Weight Wizard" +windowObject = "pyArtWeightWizardWin" + +class WeightWizard(QtWidgets.QMainWindow): + + def __init__(self, rigUiInst, parent = None): + #call base class constructor + super(WeightWizard, self).__init__(parent) + + #get the directory path of the tools + settings = QtCore.QSettings("Epic Games", "ARTv2") + self.toolsPath = settings.value("toolsPath") + self.iconsPath = settings.value("iconPath") + + #store rigUiInst in class + self.rigUiInst = rigUiInst + + #set size policy + mainSizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + + #create the main widget + self.mainWidget = QtWidgets.QWidget() + self.setCentralWidget(self.mainWidget) + + #load toolbar stylesheet + styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + f = open(styleSheetFile, "r") + self.style = f.read() + f.close() + self.setStyleSheet(self.style) + + #set qt object name + self.setObjectName(windowObject) + self.setWindowTitle(windowTitle) + + #create the mainLayout for the rig creator UI + self.layout = QtWidgets.QVBoxLayout(self.mainWidget) + + self.resize(600, 400) + self.setSizePolicy(mainSizePolicy) + self.setMinimumSize(QtCore.QSize( 600, 400 )) + self.setMaximumSize(QtCore.QSize( 600, 400 )) + + #Create a stackedWidget + self.stackWidget = QtWidgets.QStackedWidget() + self.layout.addWidget(self.stackWidget) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #PAGE ONE: SKIN PROXY GEO + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + self.page1 = QtWidgets.QFrame() + self.page1.setObjectName("dark") + self.stackWidget.addWidget(self.page1) + self.page1MainLayout = QtWidgets.QVBoxLayout(self.page1) + + + #label + pageOneLabel = QtWidgets.QLabel("Skin Weight Proxy Geometry?") + pageOneLabel.setStyleSheet("background: transparent;") + self.page1MainLayout.addWidget(pageOneLabel) + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + pageOneLabel.setFont(font) + + #image + self.pageOneInfo = QtWidgets.QFrame() + self.pageOneInfo.setMinimumSize(QtCore.QSize( 560, 250 )) + self.pageOneInfo.setMaximumSize(QtCore.QSize( 560, 250 )) + self.page1MainLayout.addWidget(self.pageOneInfo) + + image = utils.returnNicePath(self.iconsPath, "System/weightProxy.png") + self.pageOneInfo.setStyleSheet("background-image: url(" + image + ");") + + #buttons + self.pageOneButtonLayout = QtWidgets.QHBoxLayout() + self.page1MainLayout.addLayout(self.pageOneButtonLayout) + self.skinProxyFalseBtn = QtWidgets.QPushButton("No") + self.skinProxyFalseBtn.setMinimumHeight(50) + self.skinProxyFalseBtn.setFont(font) + self.skinProxyTrueBtn = QtWidgets.QPushButton("Yes") + self.skinProxyTrueBtn.setMinimumHeight(50) + self.skinProxyTrueBtn.setFont(font) + self.pageOneButtonLayout.addWidget(self.skinProxyFalseBtn) + self.pageOneButtonLayout.addWidget(self.skinProxyTrueBtn) + + self.skinProxyFalseBtn.setObjectName("blueButton") + self.skinProxyTrueBtn.setObjectName("blueButton") + + + #button hookups + self.skinProxyFalseBtn.clicked.connect(self.checkForCustomMeshes) + self.skinProxyTrueBtn.clicked.connect(self.skinProxyGeo) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #PAGE TWO: NO CUSTOM GEO FOUND + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + self.page2 = QtWidgets.QFrame() + self.stackWidget.addWidget(self.page2) + self.page2MainLayout = QtWidgets.QVBoxLayout(self.page2) + self.page2.setObjectName("dark") + + #label + pageTwoLabel = QtWidgets.QLabel("Skin Weight Custom Geometry?") + self.page2MainLayout.addWidget(pageTwoLabel) + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + pageTwoLabel.setFont(font) + + + #image + self.pageTwoInfo = QtWidgets.QFrame() + self.pageTwoInfo.setMinimumSize(QtCore.QSize( 560, 250 )) + self.pageTwoInfo.setMaximumSize(QtCore.QSize( 560, 250 )) + self.page2MainLayout.addWidget(self.pageTwoInfo) + + image = utils.returnNicePath(self.iconsPath, "System/geoNotFound.png") + self.pageTwoInfo.setStyleSheet("background-image: url(" + image + ");") + + #buttons + self.pageTwoButtonLayout = QtWidgets.QHBoxLayout() + self.page2MainLayout.addLayout(self.pageTwoButtonLayout) + self.skipStepButton = QtWidgets.QPushButton("Skip This Step") + self.skipStepButton.setMinimumHeight(50) + self.skipStepButton.setFont(font) + self.addMeshesButton = QtWidgets.QPushButton("Add Meshes") + self.addMeshesButton.setMinimumHeight(50) + self.addMeshesButton.setFont(font) + self.pageTwoButtonLayout.addWidget(self.skipStepButton) + self.pageTwoButtonLayout.addWidget(self.addMeshesButton) + self.addMeshesButton.setObjectName("blueButton") + self.skipStepButton.setObjectName("blueButton") + + #connect button + self.skipStepButton.clicked.connect(self.closeWizard) + self.addMeshesButton.clicked.connect(self.addCustomMeshes) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #PAGE THREE: SKIN CUSTOM GEO + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + self.page3 = QtWidgets.QFrame() + self.stackWidget.addWidget(self.page3) + self.page3MainLayout = QtWidgets.QVBoxLayout(self.page3) + self.page3.setObjectName("dark") + + #label + pageThreeLabel = QtWidgets.QLabel("Skin Weight Custom Geometry?") + self.page3MainLayout.addWidget(pageThreeLabel) + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + pageThreeLabel.setFont(font) + + + #image + self.pageThreeInfo = QtWidgets.QFrame() + self.pageThreeInfo.setMinimumSize(QtCore.QSize( 560, 250 )) + self.pageThreeInfo.setMaximumSize(QtCore.QSize( 560, 250 )) + self.page3MainLayout.addWidget(self.pageThreeInfo) + + image = utils.returnNicePath(self.iconsPath, "System/skinCustom.png") + self.pageThreeInfo.setStyleSheet("background-image: url(" + image + ");") + + #buttons + self.pageThreeButtonLayout = QtWidgets.QHBoxLayout() + self.page3MainLayout.addLayout(self.pageThreeButtonLayout) + self.skinGeoFalseBtn = QtWidgets.QPushButton("No") + self.skinGeoFalseBtn.setMinimumHeight(50) + self.skinGeoFalseBtn.setFont(font) + self.skinGeoTrueBtn = QtWidgets.QPushButton("Yes") + self.skinGeoTrueBtn.setMinimumHeight(50) + self.skinGeoTrueBtn.setFont(font) + self.pageThreeButtonLayout.addWidget(self.skinGeoFalseBtn) + self.pageThreeButtonLayout.addWidget(self.skinGeoTrueBtn) + self.skinGeoTrueBtn.setObjectName("blueButton") + self.skinGeoFalseBtn.setObjectName("blueButton") + + + #connect buttons + self.skinGeoTrueBtn.clicked.connect(self.skinTools) + self.skinGeoFalseBtn.clicked.connect(self.closeWizard) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #PAGE FOUR: SKIN TOOLS + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + self.page4 = QtWidgets.QFrame() + self.stackWidget.addWidget(self.page4) + self.page4TopLayout = QtWidgets.QVBoxLayout(self.page4) + self.page4.setObjectName("dark") + self.page4MainLayout = QtWidgets.QHBoxLayout() + self.page4TopLayout.addLayout(self.page4MainLayout) + + + #vertical layout for lists + self.page4VerticalLayout = QtWidgets.QVBoxLayout() + self.page4MainLayout.addLayout(self.page4VerticalLayout) + + #list widgets for meshes/joints + self.splitter = QtWidgets.QSplitter() + self.page4VerticalLayout.addWidget(self.splitter) + + #add geo list widget + self.geoLayoutWidget = QtWidgets.QWidget() + self.geoLayout = QtWidgets.QVBoxLayout(self.geoLayoutWidget) + label = QtWidgets.QLabel("Skinnable Geo:") + label.setFont(font) + self.geoLayout.addWidget(label) + + self.geoList = QtWidgets.QListWidget() + self.geoLayout.addWidget(self.geoList) + self.splitter.addWidget(self.geoLayoutWidget) + + #populate geo list + skinnableGeo = self.findCustomGeo() + for geo in skinnableGeo: + self.geoList.addItem(geo) + + #geo list signals + self.geoList.itemDoubleClicked.connect(self.selectGeo) + + #add joint list/layout + self.splitterWidget = QtWidgets.QWidget() + self.splitter.addWidget(self.splitterWidget) + self.splitterLayout = QtWidgets.QVBoxLayout(self.splitterWidget) + + self.search = QtWidgets.QLineEdit() + self.search.setPlaceholderText("Search...") + self.splitterLayout.addWidget(self.search) + self.jointList = QtWidgets.QListWidget() + self.jointList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.splitterLayout.addWidget(self.jointList) + + #populate joint list + joints = self.findJoints() + for joint in joints: + self.jointList.addItem(joint) + + #search bar + self.search.textChanged.connect(self.searchJoints) + + #add bottom layout for options/filters + self.bottomLayout = QtWidgets.QHBoxLayout() + self.bottomLayoutFrame = QtWidgets.QFrame() + self.bottomLayoutFrame.setObjectName("dark") + self.bottomLayoutFrame.setMaximumHeight(70) + self.bottomLayout.addWidget(self.bottomLayoutFrame) + self.optionsLayout = QtWidgets.QHBoxLayout(self.bottomLayoutFrame) + self.page4VerticalLayout.addLayout(self.bottomLayout) + + #add bottom options/smooth bind (max influences, maintain max, dropoff rate + + #MAX INFLUENCES + self.maxInfluenceLayout = QtWidgets.QVBoxLayout() + self.optionsLayout.addLayout(self.maxInfluenceLayout) + + label = QtWidgets.QLabel("Max Influences: ") + label.setMaximumHeight(20) + label.setStyleSheet("color: rgb(25,175,255); background:transparent;") + self.maxInfluenceLayout.addWidget(label) + + self.maxInfOptionsLayout = QtWidgets.QHBoxLayout() + self.maxInfluenceLayout.addLayout(self.maxInfOptionsLayout) + + self.maxInfluences = QtWidgets.QSlider() + self.maxInfluences.setRange(1, 8) + self.maxInfluences.setSingleStep(1) + self.maxInfluences.setPageStep(1) + self.maxInfluences.setOrientation(QtCore.Qt.Horizontal) + self.maxInfOptionsLayout.addWidget(self.maxInfluences) + + self.maxInfluencesReadout = QtWidgets.QSpinBox() + self.maxInfOptionsLayout.addWidget(self.maxInfluencesReadout) + self.maxInfluencesReadout.setReadOnly(True) + self.maxInfluencesReadout.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.maxInfluences.valueChanged.connect(self.maxInfluencesReadout.setValue) + + + #DROPOFF RATE + self.dropoffRateLayout = QtWidgets.QVBoxLayout() + self.optionsLayout.addLayout(self.dropoffRateLayout) + + label2 = QtWidgets.QLabel("Dropoff Rate:") + label2.setMaximumHeight(20) + label2.setStyleSheet("color: rgb(25,175,255); background:transparent;") + self.dropoffRateLayout.addWidget(label2) + + self.dropRateOptionsLayout = QtWidgets.QHBoxLayout() + self.dropoffRateLayout.addLayout(self.dropRateOptionsLayout) + + self.dropoffRate = QtWidgets.QSlider() + self.dropoffRate.setRange(.1, 100) + self.dropoffRate.setSingleStep(.1) + self.dropoffRate.setPageStep(1) + self.dropoffRate.setOrientation(QtCore.Qt.Horizontal) + self.dropRateOptionsLayout.addWidget(self.dropoffRate) + + self.dropoffReadout = QtWidgets.QSpinBox() + self.dropRateOptionsLayout.addWidget(self.dropoffReadout) + self.dropoffReadout.setReadOnly(True) + self.dropoffReadout.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) + self.dropoffRate.valueChanged.connect(self.dropoffReadout.setValue) + + #MAINTAIN MAX + self.maintainMaxInf = QtWidgets.QPushButton("Maintain Max Influences") + self.optionsLayout.addWidget(self.maintainMaxInf) + self.maintainMaxInf.setMinimumWidth(160) + self.maintainMaxInf.setCheckable(True) + self.maintainMaxInf.setChecked(True) + self.maintainMaxInf.clicked.connect(partial(self.toggleButtonState, self.maintainMaxInf)) + self.maintainMaxInf.setObjectName("blueButton") + + #button layout + self.buttonLayout = QtWidgets.QVBoxLayout() + self.optionsLayout.addLayout(self.buttonLayout) + + #SMOOTH BIND + self.smoothBind = QtWidgets.QPushButton("Smooth Bind") + self.smoothBind.setMinimumWidth(150) + self.smoothBind.setMinimumHeight(30) + self.buttonLayout.addWidget(self.smoothBind) + self.smoothBind.clicked.connect(self.smoothBindSelected) + self.smoothBind.setObjectName("blueButton") + + #CLOSE + self.closeBtn = QtWidgets.QPushButton("Close") + self.closeBtn.setMinimumWidth(150) + self.closeBtn.setMinimumHeight(30) + self.buttonLayout.addWidget(self.closeBtn) + self.closeBtn.clicked.connect(self.closeWizard) + self.closeBtn.setObjectName("blueButton") + + + #set values + self.dropoffRate.setValue(4) + self.maxInfluences.setValue(4) + + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + #PAGE FIVE: SKIN WEIGHTS FOUND + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + + self.page5 = QtWidgets.QFrame() + self.page5.setObjectName("dark") + self.stackWidget.addWidget(self.page5) + self.page5MainLayout = QtWidgets.QVBoxLayout(self.page5) + + + + #label + pageFiveLabel = QtWidgets.QLabel("Skin Weights Found For These Meshes:") + self.page5MainLayout.addWidget(pageFiveLabel) + font = QtGui.QFont() + font.setPointSize(12) + font.setBold(True) + pageFiveLabel.setFont(font) + + #columnLayout + self.page5ColumnLayout = QtWidgets.QHBoxLayout() + self.page5MainLayout.addLayout(self.page5ColumnLayout) + + #left column has list widget of meshes + self.page5MeshList = QtWidgets.QListWidget() + self.page5ColumnLayout.addWidget(self.page5MeshList) + self.page5MeshList.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) + self.page5MeshList.setMinimumWidth(250) + self.page5MeshList.setMaximumWidth(250) + + + #right column has import method, and import/do not import buttons + self.page5VerticalLayout = QtWidgets.QVBoxLayout() + self.page5ColumnLayout.addLayout(self.page5VerticalLayout) + + layout = QtWidgets.QHBoxLayout() + self.page5VerticalLayout.addLayout(layout) + + label = QtWidgets.QLabel("Import Method: ") + label.setStyleSheet("background: transparent;") + layout.addWidget(label) + layout.setSpacing(10) + label.setMinimumWidth(130) + label.setMaximumWidth(130) + + self.page5ImportOptions = QtWidgets.QComboBox() + self.page5ImportOptions.addItem("Vertex Order") + self.page5ImportOptions.addItem("World Position") + self.page5ImportOptions.addItem("Local Position") + self.page5ImportOptions.addItem("UV Position") + layout.addWidget(self.page5ImportOptions) + self.page5ImportOptions.setMinimumWidth(155) + self.page5ImportOptions.setMaximumWidth(155) + + self.page5VerticalLayout.addSpacerItem(QtWidgets.QSpacerItem(0, 200, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)) + + self.page5ImportWeights = QtWidgets.QPushButton("Import Weights") + self.page5ImportWeights.setMinimumHeight(50) + self.page5ImportWeights.setFont(font) + self.page5VerticalLayout.addWidget(self.page5ImportWeights) + self.page5ImportWeights.clicked.connect(partial(self.importWeights)) + self.page5ImportWeights.setObjectName("blueButton") + + self.page5IgnoreWeights = QtWidgets.QPushButton("Skip") + self.page5IgnoreWeights.setMinimumHeight(50) + self.page5IgnoreWeights.setFont(font) + self.page5VerticalLayout.addWidget(self.page5IgnoreWeights) + self.page5IgnoreWeights.clicked.connect(partial(self.skipWeightImport)) + self.page5IgnoreWeights.setObjectName("blueButton") + + + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def checkForCustomMeshes(self): + + #check for custom geometry + skinnableGeo = self.findCustomGeo() + meshes = list(skinnableGeo) + + if len(skinnableGeo) > 0: + + matches = [] + + #check to see if weight files exist for this geo on disk + for i in range(len(meshes)): + filePath = utils.returnFriendlyPath(os.path.join(cmds.internalVar(utd = True), meshes[i] + ".WEIGHTS")) + if os.path.exists(filePath): + matches.append(meshes[i]) + + if len(matches) > 0: + for match in matches: + listWidgetItem = QtWidgets.QListWidgetItem(match) + self.page5MeshList.addItem(listWidgetItem) + listWidgetItem.setSelected(True) + + #go to page 5 + self.stackWidget.setCurrentIndex(4) + + else: + #go to page 3 + self.stackWidget.setCurrentIndex(2) + + else: + #go to page 2 + self.stackWidget.setCurrentIndex(1) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def selectGeo(self): + + selected = self.geoList.selectedItems() + selected = selected[0].text() + + cmds.select(selected, r = True) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def searchJoints(self): + + #get current search terms + searchKey = self.search.text() + + #get all items in the joint list + allItems = [] + for i in range(self.jointList.count()): + item = self.jointList.item(i) + itemName = item.text() + allItems.append([item, itemName]) + + #hide all items in list + for item in allItems: + item[0].setHidden(True) + + #find items in list with search key and show item + if searchKey.find("*") == 0: + matchedItems = self.jointList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchWildcard) + for item in matchedItems: + item.setHidden(False) + + else: + matchedItems = self.jointList.findItems(searchKey, QtCore.Qt.MatchFlag.MatchContains) + for item in matchedItems: + item.setHidden(False) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def skinProxyGeo(self): + + + #get modules in scene and call the skinProxyGeo function + for module in self.rigUiInst.moduleInstances: + module.skinProxyGeo() + + + self.checkForCustomMeshes() +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def addCustomMeshes(self): + + try: + files = cmds.fileDialog2(fm = 4, okc = "Add Selected Meshes") + + if len(files) > 0: + for f in files: + try: + cmds.file(f, i = True, dns = True) + except: + cmds.warning("Could not import file: " + str(f)) + + #add new meshes to list + skinnableGeo = self.findCustomGeo() + for geo in skinnableGeo: + self.geoList.addItem(geo) + + #next page + self.stackWidget.setCurrentIndex(2) + + except: + pass + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def skinTools(self): + self.stackWidget.setCurrentIndex(3) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeWizard(self): + + mayaWindow = getMainWindow() + mayaWindow = mayaWindow.objectName() + if cmds.window(mayaWindow + "|pyArtWeightWizardWin", q = True, exists = True): + cmds.deleteUI(mayaWindow +"|pyArtWeightWizardWin") + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findCustomGeo(self): + meshes = cmds.ls(type = "mesh") + skinnableGeo = [] + + for mesh in meshes: + parent = cmds.listRelatives(mesh, parent = True, type = "transform")[0] + if parent != None: + if parent.find("proxy_geo") == -1: + if parent.find("lra") == -1: + if parent.find("bone_geo") == -1: + skinnableGeo.append(parent) + skinnableGeo = set(skinnableGeo) + return skinnableGeo + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def findJoints(self): + if cmds.objExists("root"): + cmds.select("root", hi = True) + selection = cmds.ls(sl = True) + joints = [] + + for each in selection: + if cmds.nodeType(each) == "joint": + joints.append(each) + + return joints + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def smoothBindSelected(self): + + #get selected geo from list + selectedGeo = self.geoList.selectedItems()[0] + geoRow = self.geoList.row(selectedGeo) + geo = selectedGeo.text() + + #selected joints + selectedJoints = self.jointList.selectedItems() + joints = [] + for joint in selectedJoints: + joints.append(joint.text()) + + #get smooth skin settings + maxInf = self.maxInfluences.value() + dropoff = self.dropoffRate.value() + maintainMax = self.maintainMaxInf.isChecked() + + #smooth bind + cmds.select(clear = True) + for joint in joints: + cmds.select(joint, add = True) + cmds.select(geo, add = True) + + skinCluster = cmds.skinCluster(tsb = True, maximumInfluences = maxInf, obeyMaxInfluences = maintainMax, dropoffRate = dropoff, bindMethod = 0, skinMethod = 0, normalizeWeights = 1, name = geo + "_skinCluster")[0] + + #clear selection and remove geo from list + cmds.select(clear = True) + self.geoList.takeItem(geoRow) + + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def importWeights(self): + + #get the selected items in the mesh list + selected = self.page5MeshList.selectedItems() + + #get the import method + method = self.page5ImportOptions.currentText() + + meshes = [] + for each in selected: + meshes.append(each.text()) + + for mesh in meshes: + filePath = utils.returnFriendlyPath(os.path.join(cmds.internalVar(utd = True), mesh + ".WEIGHTS")) + if os.path.exists(filePath): + riggingUtils.import_skin_weights(filePath, mesh, True) + + self.closeWizard() + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def skipWeightImport(self): + #go to page 3 + self.stackWidget.setCurrentIndex(2) + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def toggleButtonState(self, button): + + #button background image + buttonImageOn = os.path.normcase(os.path.join(self.iconsPath, "System/blue_field_background.png")) + if buttonImageOn.partition("\\")[2] != "": + buttonImageOn = buttonImageOn.replace("\\", "/") + + buttonImageOff = os.path.normcase(os.path.join(self.iconsPath, "System/field_background.png")) + if buttonImageOff.partition("\\")[2] != "": + buttonImageOff = buttonImageOff.replace("\\", "/") + + state = button.isChecked() + if state: + button.setStyleSheet("background-image: url(" + buttonImageOn + "); background-color: rgb(25,175,255)") + + else: + button.setStyleSheet("background-image: url(" + buttonImageOff + "); background-color: rgb(0,0,0)") + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + def closeWizard(self): + mayaWindow = getMainWindow() + mayaWindow = mayaWindow.objectName() + if cmds.window(mayaWindow + "|pyArtWeightWizardWin", q = True, exists = True): + cmds.deleteUI(mayaWindow +"|pyArtWeightWizardWin") +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +def run(rigUiInst): + mayaWindow = getMainWindow() + mayaWindow = mayaWindow.objectName() + if cmds.window(mayaWindow + "|pyArtWeightWizardWin", q = True, exists = True): + cmds.deleteUI(mayaWindow +"|pyArtWeightWizardWin") + + gui = WeightWizard(rigUiInst, getMainWindow()) + gui.show() +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +def runOnlySkinTools(rigUiInst): + mayaWindow = getMainWindow() + mayaWindow = mayaWindow.objectName() + if cmds.window(mayaWindow + "|pyArtWeightWizardWin", q = True, exists = True): + cmds.deleteUI(mayaWindow +"|pyArtWeightWizardWin") + + gui = WeightWizard(rigUiInst, getMainWindow()) + gui.show() + + gui.stackWidget.setCurrentIndex(3) diff --git a/Core/Scripts/Interfaces/StyleSheets/animPicker.qss b/Core/Scripts/Interfaces/StyleSheets/animPicker.qss new file mode 100644 index 0000000..f9bc0aa --- /dev/null +++ b/Core/Scripts/Interfaces/StyleSheets/animPicker.qss @@ -0,0 +1,770 @@ +/* PROPERTIES */ + +*[grayBackground="true"] {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(33,34,26));} +*[blueBackground="true"] {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); color: rgb(255,255,255);} +*[boldFont="true"] {font: bold 12px;} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QInputDialog */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QInputDialog +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/addModule_background.png); + color: rgb(202, 207, 210); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QWidget */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QWidget +{ + background-color: transparent; + color: rgb(202, 207, 210); +} + + +QWidget#darkGrey +{ + background-color: rgb(45,45,45); + color: rgb(202, 207, 210); +} + +QWidget#darkImg +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTreeView */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QTreeView +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +QTreeWidget +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTableWidget */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QTableWidget QTableCornerButton::section +{ + background: black; + border: 2px outset black; +} + + QTableWidget +{ + border:2px groove black; + border-radius:3px; + selection-color:black; +} + + +QTableWidget::item +{ + padding:5px; + outline:0px; + background-color: rgb(90,90,90); + color: white; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QMenu */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QMenuBar +{ + background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: solid 2px black; + border-width: 2px; +} + +QMenuBar::item +{ + spacing: 3px; /* spacing between menu bar items */ + padding: 1px 4px; + border-radius: 4px; + font: bold 10px; +} + + +QMenuBar::item:pressed +{ + background: rgb(30,30,30); +} + +QMenu +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: 2px solid black; + border-radius: 4px; +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTABBAR */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +QTabBar::tab +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + padding-left: 5px; + padding-right: -10px; + padding-top: 5px; + padding-bottom: 5px; +} + +QTabBar::tab:selected +{ + background: rgb(25,175,255); + +} + + +QTabBar::tab:hover +{ + + background: rgb(40,196,255); +} + + +QTabBar::tab:!selected +{ + margin-top: 5px; +} + +QTabWidget::pane +{ + border-top: 2px solid rgb(25,175,255); + border-left: 2px solid rgb(25,175,255); + border-right: 2px solid rgb(25,175,255); + border-bottom: 2px solid rgb(25,175,255); +} + + + + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QToolTip */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QToolTip +{ + background-color: black; + color: white; + border: black solid 1px; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QProgressBar */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QProgressBar +{ + border: 2px solid rgb(9,62,98); + border-radius: 5px; + text-align: center; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + font: bold 14px; +} + +QProgressBar::chunk +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(9,62,98)); + width: 10px; + margin: 0.5px; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QFrames */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QFrame +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/addModule_background.png); +} + +QFrame#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +QFrame#mid +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/mid_epic_background.png); +} + + +QFrame#light +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + border: 2px solid rgb(25,175,255); +} + +QFrame#epic +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/animPicker.png); +} + +QFrame#epic2 +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/animToolbar.png); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QScrollArea */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QScrollArea +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + border: 2px solid rgb(25,175,255); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QSplitter */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QSplitter +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QStackedWidget */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QStackedWidget +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +QStackedWidget#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QGroupBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QGroupBox#light +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); +} + +QGroupBox +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + border: 2px solid rgb(15,107,162); + border-radius: 5px; + padding-top: 5px; + padding-bottom: 5px; + font: bold 12px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + padding: 0 3px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #FFOECE, stop: 1 #FFFFFF); + font: bold 12px; +} + +QGroupBox::indicator:unchecked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/upArrow.png); +} + +QGroupBox::indicator:checked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/downArrow.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QSlider */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QSlider +{ + background-color: transparent; + color: black; +} + +QSlider::groove:horizontal +{ + height: 6px; + background: black; +} + +QSlider::groove:vertical +{ + height: 6px; + background: black; +} + + +QSlider::handle:horizontal +{ + background: rgb(25,175,255); + border: 1px solid black; + width: 18px; + margin: -4px 0; + border-radius: 3px; +} + +QSlider::handle:vertical +{ + background: rgb(25,175,255); + border: 1px solid black; + width: 18px; + margin: -4px 0; + border-radius: 3px; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QPushButtons */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + /* Gray */ + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QPushButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background.png); + color: white; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; + border-radius: 5px; +} + + +QPushButton:hover +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background_hover.png); +} + + +QPushButton:pressed +{ + margin-top: 2px; +} + + +QPushButton:checked +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); + color: white; +} + + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + /* Green */ + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QPushButton#greenButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/green_field_background.png); + color: white; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; + border-radius: 5px; +} + + +QPushButton:hover#greenButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/green_field_background_hover.png); +} + + +QPushButton:pressed#greenButton +{ + margin-top: 2px; +} + + +QPushButton:checked#greenButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background_hover.png); + color: white; +} + + + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + /* Blue */ + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QPushButton#blueButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: rgb(255,255,255); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: bold 12px; +} + + +QPushButton:hover#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QPushButton:pressed#blueButton +{ + margin-top: 2px; +} + + +QPushButton:disabled#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background.png); +} + +QPushButton:checked#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + + + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + /* Toolbar Variant */ + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QPushButton#toolbar +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: rgb(255,255,255); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 65 20pt; +} + + +QPushButton:hover#toolbar +{ + margin-top: -2px; +} + + +QPushButton:pressed#toolbar +{ + margin-top: 2px; +} + + + + +QPushButton:checked#toolbar +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTextEdit */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QTextEdit +{ + color: white; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + +} + +QTextEdit#light +{ + color: white; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QCheckBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QCheckBox +{ + spacing: 5px; + margin-top: 2px; + color: rgb(25,175,255); + font: bold 12px; +} + +QCheckBox::hover +{ + color: rgb(255,255,255); +} + +QCheckBox::indicator:unchecked +{ + background-color: rgb(120,120,120); +} + +QCheckBox::indicator:checked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/checked.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QListWidget */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QListView +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + font: bold 12px; +} + +QListView::item:selected +{ + background: rgb(25,175,255); + color: rgb(30,30,30); + border: 1px solid rgb(30,30,30); +} + + +QListView::item:selected:active +{ + background: rgb(25,175,255); + color: rgb(30,30,30); +} + +QListView::item:hover +{ + background: rgb(80,80,80); + border: 1px solid rgb(30,30,30); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QSpinBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QSpinBox +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: black; +} + + + +QDoubleSpinBox +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: black; +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QLineEdit */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +QLineEdit +{ + border: 2px solid rgb(0,0,0); + border-radius: 5px; + background-color: rgb(80,80,80); + font: bold 12px; + selection-background-color: black; +} + +QLineEdit::focus +{ + border: 1px solid rgb(25,175,255); + +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QComboBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QComboBox +{ + border: 2px solid black; + border-radius: 4px; + background-color: rgb(80,80,80); + +} + + +QComboBox:on +{ + padding-top: 3px; + padding-left: 4px; +} + + +QComboBox:editable +{ + background: rgb(60,60,60); +} + + +QComboBox QAbstractItemView +{ + border: 2px solid black; + selection-background-color: rgb(25,175,255); + border-width: 2px; +} + + +QComboBox::down-arrow +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/downArrow.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QRadioButton */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +QRadioButton +{ + spacing: 5px; + margin-top: 2px; + color: rgb(255,255,255); + padding: 2px; + border-radius: 10px; +} + +QRadioButton::hover +{ + color: rgb(25,175,255); +} + +QRadioButton::indicator +{ + background-color: rgb(80,80,80); + border-radius: 10px; + border: 2px solid black; +} + +QRadioButton::indicator:checked +{ + background-color: rgb(25,175,255); + +} + +QRadioButton::indicator:unchecked:hover +{ + background-color: rgb(120,120,120); + +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* Qlabel */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QLabel +{ + background-color: transparent; +} + diff --git a/Core/Scripts/Interfaces/StyleSheets/mainScheme - Copy.qss b/Core/Scripts/Interfaces/StyleSheets/mainScheme - Copy.qss new file mode 100644 index 0000000..725a009 --- /dev/null +++ b/Core/Scripts/Interfaces/StyleSheets/mainScheme - Copy.qss @@ -0,0 +1,645 @@ +QWidget +{ + background-color: transparent; + color: rgb(202, 207, 210); +} + + +QWidget#darkGrey +{ + background-color: rgb(45,45,45); + color: rgb(202, 207, 210); +} + +QWidget#darkImg +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +QTreeView +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +QTreeWidget +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + + +QTableWidget QTableCornerButton::section +{ + background: black; + border: 2px outset black; +} + + QTableWidget +{ + border:2px groove black; + border-radius:3px; + selection-color:black; +} + + +QTableWidget::item +{ + padding:5px; + outline:0px; + background-color: rgb(90,90,90); + color: white; +} + + +QMenuBar +{ + background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: solid 2px black; + border-width: 2px; +} + +QMenuBar::item +{ + spacing: 3px; /* spacing between menu bar items */ + padding: 1px 4px; + border-radius: 4px; + font: bold 10px; +} + + +QMenuBar::item:pressed +{ + background: rgb(30,30,30); +} + +QMenu +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: 2px solid black; + border-radius: 4px; +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTABBAR */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +QTabBar::tab +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + padding-left: 5px; + padding-right: -10px; + padding-top: 5px; + padding-bottom: 5px; +} + +QTabBar::tab:selected +{ + background: rgb(25,175,255); + +} + + +QTabBar::tab:hover +{ + + background: rgb(40,196,255); +} + + +QTabBar::tab:!selected +{ + margin-top: 5px; +} + +QTabWidget::pane +{ + border-top: 2px solid rgb(25,175,255); + border-left: 2px solid rgb(25,175,255); + border-right: 2px solid rgb(25,175,255); + border-bottom: 2px solid rgb(25,175,255); +} + + + + +QToolTip +{ + background-color: black; + color: white; + border: black solid 1px; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + + +QProgressBar +{ + border: 2px solid rgb(9,62,98); + border-radius: 5px; + text-align: center; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + font: bold 14px; +} + +QProgressBar::chunk +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(9,62,98)); + width: 10px; + margin: 0.5px; +} + +QFrame +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/addModule_background.png); +} + +QFrame#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +QFrame#mid +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/mid_epic_background.png); +} + + +QFrame#light +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + border: 2px solid rgb(25,175,255); +} + +QFrame#epic +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + + + +QScrollArea +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + border: 2px solid rgb(25,175,255); +} + + +QSplitter +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +QStackedWidget +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); +} + +QStackedWidget#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +/* PROPERTIES */ + +*[grayBackground="true"] {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(33,34,26));} +*[blueBackground="true"] {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); color: rgb(255,255,255);} +*[boldFont="true"] {font: bold 12px;} + + +QGroupBox +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/addModule_background.png); +} + +QGroupBox#light +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); +} + +QSlider +{ + background-color: transparent; + color: black; +} + +QSlider::groove:horizontal +{ + height: 6px; + background: black; +} + +QSlider::groove:vertical +{ + height: 6px; + background: black; +} + + +QSlider::handle:horizontal +{ + background: rgb(25,175,255); + border: 1px solid black; + width: 18px; + margin: -4px 0; + border-radius: 3px; +} + +QSlider::handle:vertical +{ + background: rgb(25,175,255); + border: 1px solid black; + width: 18px; + margin: -4px 0; + border-radius: 3px; +} + + + +/* */ +/* GRAY QPUSHBUTTON */ +/* */ + +QPushButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background.png); + color: white; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; + border-radius: 5px; +} + + +QPushButton:hover +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background_hover.png); +} + + +QPushButton:pressed +{ + margin-top: 2px; +} + + +QPushButton:checked +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); + color: white; +} + + +/* */ +/* GREEN QPUSHBUTTON */ +/* */ + +QPushButton#greenButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/green_field_background.png); + color: white; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; + border-radius: 5px; +} + + +QPushButton:hover#greenButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/green_field_background_hover.png); +} + + +QPushButton:pressed#greenButton +{ + margin-top: 2px; +} + + +QPushButton:checked#greenButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background_hover.png); + color: white; +} + + + +/* */ +/* BLUE QPUSHBUTTON */ +/* */ + +QPushButton#blueButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: rgb(255,255,255); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: bold 12px; +} + + +QPushButton:hover#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QPushButton:pressed#blueButton +{ + margin-top: 2px; +} + + + + +QPushButton:checked#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + + +/* */ +/* BLUE QPUSHBUTTON SPECIAL */ +/* */ + +QPushButton#blueButtonSpecial +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: rgb(255,255,255); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 65 20pt; +} + + +QPushButton:hover#blueButtonSpecial +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QPushButton:pressed#blueButtonSpecial +{ + margin-top: 2px; +} + + + + +QPushButton:checked#blueButtonSpecial +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QTextEdit +{ + color: white; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + +} + +QTextEdit#light +{ + color: white; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + +} + +/* */ +/* QCHECKBOX */ +/* */ + +QCheckBox +{ + spacing: 5px; + margin-top: 2px; + color: rgb(25,175,255); + font: bold 12px; +} + +QCheckBox::hover +{ + color: rgb(255,255,255); +} + +QCheckBox::indicator:unchecked +{ + background-color: rgb(120,120,120); +} + +QCheckBox::indicator:checked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/checked.png); +} + + + + + + +/* */ +/* QLISTWIDGET */ +/* */ + +QListView +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + font: bold 12px; +} + +QListView::item:selected +{ + border: 1px solid rgb(30,30,30); +} + + +QListView::item:selected:active +{ + background: rgb(25,175,255); + color: rgb(30,30,30); +} + +QListView::item:hover +{ + background: rgb(80,80,80); + border: 1px solid rgb(30,30,30); +} + + + + + + + + +/* */ +/* QSPINBOX */ +/* */ + +QSpinBox +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: black; +} + + + +QDoubleSpinBox +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: black; +} + + + + +/* */ +/* QLINEEDIT */ +/* */ + + +QLineEdit +{ + border: 2px solid rgb(0,0,0); + border-radius: 5px; + background-color: rgb(80,80,80); + font: bold 12px; + selection-background-color: black; +} + +QLineEdit::focus +{ + border: 1px solid rgb(25,175,255); + +} + + + + + + + +/* */ +/* QCOMBOBOX */ +/* */ + +QComboBox +{ + border: 2px solid black; + border-radius: 4px; + background-color: rgb(80,80,80); + +} + + +QComboBox:on +{ + padding-top: 3px; + padding-left: 4px; +} + + +QComboBox:editable +{ + background: rgb(60,60,60); +} + + +QComboBox QAbstractItemView +{ + border: 2px solid black; + selection-background-color: rgb(25,175,255); + border-width: 2px; +} + + +QComboBox::down-arrow +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/downArrow.png); +} + + + + + + + +/* */ +/* QRADIOBUTTON */ +/* */ + + +QRadioButton +{ + spacing: 5px; + margin-top: 2px; + color: rgb(255,255,255); + padding: 2px; + border-radius: 10px; +} + +QRadioButton::hover +{ + color: rgb(25,175,255); +} + +QRadioButton::indicator +{ + background-color: rgb(80,80,80); + border-radius: 10px; + border: 2px solid black; +} + +QRadioButton::indicator:checked +{ + background-color: rgb(25,175,255); + +} + +QRadioButton::indicator:unchecked:hover +{ + background-color: rgb(120,120,120); + +} + + + +QLabel +{ + background: transparent; + +}
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/StyleSheets/mainScheme.qss b/Core/Scripts/Interfaces/StyleSheets/mainScheme.qss new file mode 100644 index 0000000..19b292f --- /dev/null +++ b/Core/Scripts/Interfaces/StyleSheets/mainScheme.qss @@ -0,0 +1,900 @@ + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QWidget */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QWidget +{ + background: transparent; + color: rgb(202, 207, 210); +} + + +QWidget#darkGrey +{ + background-color: rgb(45,45,45); + color: rgb(202, 207, 210); +} + +QWidget#darkImg +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTreeView */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QTreeView +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +QTreeWidget +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +QTreeWidget::item +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + color: rgb(255, 255, 255); +} + +QTreeWidget::item:hover +{ + color: rgb(255,174,0); +} + +QTreeWidget::item:selected +{ + color: rgb(25,175,255); +} + + +QTreeView::branch { + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + color: white; +} + + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/upArrow.png); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/downArrow.png); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTableWidget */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QTableWidget QTableCornerButton::section +{ + background: black; + border: 2px outset #444444; +} + + QTableWidget +{ + border:2px groove black; + border-radius:3px; + selection-color:black; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + + +QTableWidget::item +{ + padding:5px; + outline:0px; + background-color: rgb(90,90,90); + color: white; +} + + +QHeaderView::section { + background-color: black; + padding: 4px; + border-style: none; + border-bottom: 1px solid #444444; + border-right: 1px solid #444444; +} + +QHeaderView::section:horizontal +{ + border-top: 1px solid #444444; +} + +QHeaderView::section:vertical +{ + border-left: 1px solid #444444; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QMenuBar */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QMenuBar +{ + background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: solid 2px black; + border-width: 2px; +} + +QMenuBar::item +{ + spacing: 3px; /* spacing between menu bar items */ + padding: 1px 4px; + border-radius: 4px; + font: bold 10px; +} + + +QMenuBar::item:pressed +{ + background: rgb(30,30,30); +} + +QMenu +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: 2px solid black; + border-radius: 4px; +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTABBAR */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +QTabBar::tab +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + padding-left: 5px; + padding-right: -10px; + padding-top: 5px; + padding-bottom: 5px; +} + +QTabBar::tab:selected +{ + background: rgb(25,175,255); + +} + + +QTabBar::tab:hover +{ + + background: rgb(40,196,255); +} + + +QTabBar::tab:!selected +{ + margin-top: 5px; +} + +QTabWidget::pane +{ + border-top: 2px solid rgb(25,175,255); + border-left: 2px solid rgb(25,175,255); + border-right: 2px solid rgb(25,175,255); + border-bottom: 2px solid rgb(25,175,255); +} + + + + + +QTabBar::tab#dark +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + width: 400px; + padding-left: -10px; +} +QTabBar::tab:selected#dark +{ + background-color: rgb(14,100,143); + border: 2px solid black; +} +QTabBar::tab:hover#dark +{ + background: rgb(19,132,183); +} +QTabBar::tab:!selected#dark +{ + margin-top: 5px; +} +QTabWidget::pane#dark +{ + border-top: 2px solid rgb(19,132,183); +} + + + + +QTabBar::tab#orange +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(90,90,90), stop:1 rgb(30,30,30)); + border: 2px solid black; + width: 180px; + padding-left: -10px; + font: 8pt; +} +QTabBar::tab:selected#orange +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(255,174,0), stop:1 rgb(30,30,30)); + border: 2px solid black; + font: bold 10pt; +} +QTabBar::tab:hover#orange +{ + background: rgb(132,95,16); + font: bold 10pt; +} +QTabBar::tab:!selected#orange +{ + margin-top: 5px; +} +QTabWidget::pane#orange +{ + border: 2px solid rgb(0,0,0); +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QToolTip */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QToolTip +{ + background-color: black; + color: white; + border: black solid 1px; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QProgressBAr */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QProgressBar +{ + border: 2px solid rgb(9,62,98); + border-radius: 5px; + text-align: center; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + font: bold 14px; +} + +QProgressBar::chunk +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(9,62,98)); + width: 10px; + margin: 0.5px; +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QFrame */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QFrame +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/addModule_background.png); +} + +QFrame#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +QFrame#dark2 +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + border: 2px solid rgb(15,107,162); +} + +QFrame#mid +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/mid_epic_background.png); +} + + +QFrame#light +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + border: 2px solid rgb(25,175,255); +} + +QFrame#epic +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + +QFrame#clear +{ + background: transparent; +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QScrollArea */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QScrollArea +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + border: 2px solid rgb(25,175,255); +} + + +QScrollArea#epic +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + border: 2px solid rgb(0,0,0); +} + + +QScrollBar:handle:vertical +{ + background-color: qlineargradient(spread:pad, x1:0.506, y1:0.835136, x2:0.4835, y2:0.005, stop:0 rgba(40, 40, 40, 255), stop:1 rgba(120, 120, 120, 255)); + border: 1px solid rgb(15, 107, 172); +} + + +QScrollBar:vertical +{ + background-color: rgb(120,120,120); + color: rgb(25,175,255); +} + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QSplitter */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QSplitter +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QStackedWidget */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QStackedWidget +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); +} + +QStackedWidget#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* PROPERTIES */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* PROPERTIES */ + +*[grayBackground="true"] {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(33,34,26));} +*[blueBackground="true"] {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); color: rgb(255,255,255);} +*[boldFont="true"] {font: bold 12px;} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QGroupBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QGroupBox +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/addModule_background.png); +} + +QGroupBox#light +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QSlider */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QSlider +{ + background-color: transparent; + background-color: transparent;dow + color: black; +} + +QSlider::groove:horizontal +{ + height: 6px; + background: black; +} + +QSlider::groove:vertical +{ + height: 6px; + background: black; +} + + +QSlider::handle:horizontal +{ + background: rgb(25,175,255); + border: 1px solid black; + width: 18px; + margin: -4px 0; + border-radius: 3px; +} + +QSlider::handle:vertical +{ + background: rgb(25,175,255); + border: 1px solid black; + width: 18px; + margin: -4px 0; + border-radius: 3px; +} + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QPushButtons */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* */ +/* GRAY QPUSHBUTTON */ +/* */ + +QPushButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background.png); + color: white; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; + border-radius: 5px; +} + + +QPushButton:hover +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background_hover.png); +} + + +QPushButton:pressed +{ + margin-top: 2px; +} + + +QPushButton:checked +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); + color: white; +} + + +/* */ +/* GREEN QPUSHBUTTON */ +/* */ + +QPushButton#greenButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/green_field_background.png); + color: white; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; + border-radius: 5px; +} + + +QPushButton:hover#greenButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/green_field_background_hover.png); +} + + +QPushButton:pressed#greenButton +{ + margin-top: 2px; +} + + +QPushButton:checked#greenButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background_hover.png); + color: white; +} + + + +/* */ +/* BLUE QPUSHBUTTON */ +/* */ + +QPushButton#blueButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: rgb(255,255,255); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: bold 12px; +} + + +QPushButton:hover#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QPushButton:pressed#blueButton +{ + margin-top: 2px; +} + + + + +QPushButton:checked#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + + +/* */ +/* BLUE QPUSHBUTTON SPECIAL */ +/* */ + +QPushButton#blueButtonSpecial +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: rgb(255,255,255); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 65 20pt; +} + + +QPushButton:hover#blueButtonSpecial +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QPushButton:pressed#blueButtonSpecial +{ + margin-top: 2px; +} + + + + +QPushButton:checked#blueButtonSpecial +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + + + + +/* */ +/* TOOLBAR QPUSHBUTTON */ +/* */ +QPushButton:hover#toolbar +{ + margin-top: -4px; + border: 4px solid rgb(25,175,255); +} + +QPushButton:pressed#toolbar +{ + margin-top: 2px; +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTextEdit */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QTextEdit +{ + color: white; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); + +} + +QTextEdit#light +{ + color: white; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + +} + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QCheckBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QCheckBox +{ + spacing: 5px; + margin-top: 2px; + color: rgb(25,175,255); + font: bold 12px; +} + +QCheckBox::hover +{ + color: rgb(255,255,255); +} + +QCheckBox::indicator:unchecked +{ + background-color: rgb(120,120,120); +} + +QCheckBox::indicator:checked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/checked.png); +} + + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QListView */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +QListView +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + font: bold 12px; +} + +QListView::item:selected +{ + border: 1px solid rgb(30,30,30); +} + + +QListView::item:selected:active +{ + background: rgb(25,175,255); + color: rgb(30,30,30); +} + +QListView::item:hover +{ + background: rgb(80,80,80); + border: 1px solid rgb(30,30,30); +} + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QSpinBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QSpinBox +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: black; +} + + + +QDoubleSpinBox +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: black; +} + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QLineEdit */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +QLineEdit +{ + border: 2px solid rgb(0,0,0); + border-radius: 5px; + background-color: rgb(80,80,80); + font: bold 12px; + selection-background-color: black; +} + +QLineEdit::focus +{ + border: 1px solid rgb(25,175,255); + +} + + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QComboBox */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QComboBox +{ + border: 2px solid black; + border-radius: 4px; + background-color: rgb(80,80,80); + +} + + +QComboBox:on +{ + padding-top: 3px; + padding-left: 4px; +} + + +QComboBox:editable +{ + background: rgb(60,60,60); +} + + +QComboBox QAbstractItemView +{ + border: 2px solid black; + selection-background-color: rgb(25,175,255); + border-width: 2px; +} + + +QComboBox::down-arrow +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/downArrow.png); +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QRadioButton */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QRadioButton +{ + spacing: 5px; + margin-top: 2px; + color: rgb(255,255,255); + padding: 2px; + border-radius: 10px; +} + +QRadioButton::hover +{ + color: rgb(25,175,255); +} + +QRadioButton::indicator +{ + background-color: rgb(80,80,80); + border-radius: 10px; + border: 2px solid black; +} + +QRadioButton::indicator:checked +{ + background-color: rgb(25,175,255); + +} + +QRadioButton::indicator:unchecked:hover +{ + background-color: rgb(120,120,120); + +} + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QLabel */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +QLabel +{ + background: transparent; + +}
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/StyleSheets/modules.qss b/Core/Scripts/Interfaces/StyleSheets/modules.qss new file mode 100644 index 0000000..7088426 --- /dev/null +++ b/Core/Scripts/Interfaces/StyleSheets/modules.qss @@ -0,0 +1,9 @@ +QPushButton:hover +{ + margin-top: -2px; +} + +QPushButton:pressed +{ + margin-top: 2px; +}
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/StyleSheets/skeletonSettings.qss b/Core/Scripts/Interfaces/StyleSheets/skeletonSettings.qss new file mode 100644 index 0000000..dc5ad0e --- /dev/null +++ b/Core/Scripts/Interfaces/StyleSheets/skeletonSettings.qss @@ -0,0 +1,404 @@ + +QWidget +{ + background: transparent; +} + +QFrame +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); +} + + +QGroupBox +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + border: 2px solid rgb(15,107,162); + border-radius: 5px; + padding-top: 5px; + padding-bottom: 5px; + font: bold 12px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + padding: 0 3px; + background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #FFOECE, stop: 1 #FFFFFF); + font: bold 12px; +} + +QGroupBox::indicator:unchecked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/upArrow.png); +} + +QGroupBox::indicator:checked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/downArrow.png); +} + + +/* */ +/* GRAY QPUSHBUTTON */ +/* */ + +QPushButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: white; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; + border-radius: 5px; +} + + +QPushButton:enabled:hover +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QPushButton:pressed +{ + margin-top: 2px; +} + + +QPushButton:checked +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); + color: white; +} + +QPushButton:disabled +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/grey_field_background.png); + color: white; +} + + +QPushButton:enabled +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: white; +} + + +/* */ +/* BLUE QPUSHBUTTON */ +/* */ + +QPushButton#blueButton +{ + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(25,175,255), stop:1 rgb(12,63,91)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background.png); + color: rgb(255,255,255); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + padding-top: 5px; + padding-bottom: 5px; + font: 12px; +} + + +QPushButton:hover#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + +QPushButton:pressed#blueButton +{ + margin-top: 2px; +} + + + + +QPushButton:checked#blueButton +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/blue_field_background_hover.png); +} + + + +/* */ +/* QCHECKBOX */ +/* */ + +QCheckBox +{ + spacing: 5px; + margin-top: 2px; + color: rgb white; + font: 12px; + background-color: transparent; + background: transparent; +} + +QCheckBox::hover +{ + color: rgb(255,255,255); +} + +QCheckBox::indicator:unchecked +{ + background-color: white; + border: 2px solid black; +} + +QCheckBox::indicator:checked +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/checked.png); +} + + + + + + + +/* */ +/* QLISTWIDGET */ +/* */ + +QListView +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + border-radius: 5px; + border: 2px solid rgb(9,10,12); + shadow: rgb(9,10,12); + font: bold 12px; +} + +QListView::item:selected +{ + border: 1px solid rgb(30,30,30); +} + + +QListView::item:selected:active +{ + background: rgb(25,175,255); + color: rgb(30,30,30); +} + +QListView::item:hover +{ + background: rgb(80,80,80); + border: 1px solid rgb(30,30,30); +} + + + + + + + + +/* */ +/* QSPINBOX */ +/* */ + +QSpinBox +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: rgb(25,175,255); +} + +QSpinBox::enabled +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: rgb(25,175,255); +} + + +QSpinBox::disabled +{ + padding-right: 15px; + border: 2px solid rgb(9,10,12); + border-radius: 5px; + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(80,80,80), stop:1 rgb(60,60,60)); + font: bold 12px; + selection-background-color: rgb(25,175,255); +} + + + + + + +QTabBar::tab +{ + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(60, 60, 60, 255), stop:1 rgba(30, 30, 30, 255)); + padding-left: 10px; + padding-right: 10px; + padding-top: 5px; + padding-bottom: 5px; +} + + +QTabBar::tab:selected +{ + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(120,120,120, 255), stop:1 rgba(60,60,60, 255)); +} + +QTabBar::tab:hover +{ + background: rgb(180,180,180); + color: black; +} + +QTabBar::tab:!selected +{ + margin-top: 1px; +} + + + + + +/* */ +/* QLINEEDIT */ +/* */ + + +QLineEdit +{ + border: 2px solid rgb(0,0,0); + border-radius: 5px; + background-color: rgb(80,80,80); + font: bold 12px; + selection-background-color: black; +} + +QLineEdit::focus +{ + border: 1px solid rgb(25,175,255); + +} + + + + + + + +/* */ +/* QCOMBOBOX */ +/* */ + +QComboBox +{ + border: 2px solid black; + border-radius: 4px; + background-color: rgb(80,80,80); + +} + + +QComboBox:on +{ + padding-top: 3px; + padding-left: 4px; +} + + +QComboBox:editable +{ + background: rgb(60,60,60); +} + + +QComboBox QAbstractItemView +{ + border: 2px solid black; + selection-background-color: rgb(25,175,255); + border-width: 2px; +} + + +QComboBox::down-arrow +{ + image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/downArrow.png); +} + + + + + + + +/* */ +/* QRADIOBUTTON */ +/* */ + + +QRadioButton +{ + spacing: 5px; + margin-top: 2px; + color: rgb(255,255,255); + padding: 2px; + border-radius: 10px; + background: transparent; +} + +QRadioButton::hover +{ + color: rgb(25,175,255); +} + +QRadioButton::indicator +{ + background-color: rgb(80,80,80); + border-radius: 10px; + border: 2px solid black; +} + +QRadioButton::indicator:checked +{ + background-color: rgb(25,175,255); + +} + +QRadioButton::indicator:unchecked:hover +{ + background-color: rgb(120,120,120); + +} + + + + +/* */ +/* QLABEL */ +/* */ + +QLabel +{ + background-color: transparent; +} + + + + + +/* styleSheetFile = utils.returnNicePath(self.toolsPath, "Core/Scripts/Interfaces/StyleSheets/mainScheme.qss") + with open(styleSheetFile, "r") as f: + self.groupBox.setStyleSheet(f.read()) */
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/StyleSheets/toolbarButtonsScheme.qss b/Core/Scripts/Interfaces/StyleSheets/toolbarButtonsScheme.qss new file mode 100644 index 0000000..ef6b5c0 --- /dev/null +++ b/Core/Scripts/Interfaces/StyleSheets/toolbarButtonsScheme.qss @@ -0,0 +1,134 @@ +QPushButton:hover +{ + margin-top: -2px; + border: 4px solid rgb(25,175,255); +} + +QPushButton:pressed +{ + margin-top: 2px; +} + + + +QMenuBar +{ + background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: solid 2px black; + border-width: 2px; +} + +QMenuBar::item +{ + spacing: 3px; /* spacing between menu bar items */ + padding: 1px 4px; + border-radius: 4px; + font: bold 10px; +} + + +QMenuBar::item:pressed +{ + background: rgb(30,30,30); +} + +QMenu +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(60,60,60), stop:1 rgb(30,30,30)); + border: 2px solid black; + border-radius: 4px; +} + +QToolTip +{ + background-color: black; + color: white; + border: black solid 1px; + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + +QFrame#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +QFrame#mid +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/mid_epic_background.png); +} + +QFrame#light +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); + border: 2px solid rgb(25,175,255); +} + +QFrame#epic +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); +} + + + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* QTABBAR */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + + +QTabBar::tab +{ + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(19,132,183), stop:1 rgb(30,30,30)); + +} + + +QTabBar::tab:selected +{ + background: rgb(25,175,255); +} + + +QTabBar::tab:hover +{ + background: rgb(40,196,255); +} + + +QTabBar::tab:!selected +{ + margin-top: 5px; +} + + + + +QWidget#darkGrey +{ + background-color: rgb(45,45,45); + color: rgb(202, 207, 210); +} + +QWidget#darkImg +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/light_epic_background.png); + color: rgb(202, 207, 210); +} + +QWidget +{ + background-color: transparent; + color: rgb(202, 207, 210); +} + + +QStackedWidget#dark +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background_dark.png); +} + +QStackedWidget +{ + background-image: url(C:/Users/jeremy/Documents/BitBucket/ARTv2/ARTv2/Core/Icons/System/field_background.png); +}
\ No newline at end of file diff --git a/Core/Scripts/Interfaces/__init__.py b/Core/Scripts/Interfaces/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Core/Scripts/Interfaces/__init__.py |