def exportSystemTemplate(self): if self._currentObject is None: QMessageBox.warning(self, 'Warning', 'Must have a current object selection') return if blurdev is None: pref = QSettings("Blur", "Simplex3") defaultPath = str(toPyObject(pref.value('systemExport', os.path.join(os.path.expanduser('~'))))) path = self.fileDialog("Export Template", defaultPath, ["smpx", "json"], save=True) if not path: return pref.setValue('systemExport', os.path.dirname(path)) pref.sync() else: # Blur Prefs pref = blurdev.prefs.find('tools/simplex3') defaultPath = pref.restoreProperty('systemExport', os.path.join(os.path.expanduser('~'))) path = self.fileDialog("Export Template", defaultPath, ["smpx", "json"], save=True) if not path: return pref.recordProperty('systemExport', os.path.dirname(path)) pref.save() if path.endswith('.smpx'): pBar = QProgressDialog("Exporting smpx File", "Cancel", 0, 100, self) pBar.show() self.simplex.exportAbc(path, pBar) pBar.close() elif path.endswith('.json'): dump = self.simplex.dump() with open(path, 'w') as f: f.write(dump)
def updateRestShapeInterface(window): sel = cmds.ls(sl=True) if not sel: QMessageBox.warning(window, "Nothing Selected", "Nothing Selected") return sel = sel[0] mesh = window.simplex.DCC.mesh # TODO, Check vert number and blendshape input connections selVerts = cmds.polyEvaluate(sel, vertex=1) meshVerts = cmds.polyEvaluate(mesh, vertex=1) if selVerts != meshVerts: msg = "Selected object {0} has {1} verts\nBase Object has {2} verts".format( sel, selVerts, meshVerts) QMessageBox.warning(window, "Vert Mismatch", msg) return # TODO Check for live connections bs = window.simplex.DCC.shapeNode cnx = cmds.listConnections(bs, plugs=1, destination=0, type='mesh') if cnx: cnxs = ', '.join([i.split('.')[0] for i in cnx]) cnxs = textwrap.fill(cnxs) msg = [ "Some shapes have a live input connection:", cnxs, "", "These shapes will not get the update.", "Continue anyway?" ] msg = '\n'.join(msg) btns = QMessageBox.Ok | QMessageBox.Cancel bret = QMessageBox.question(window, "Live Connections", msg, btns) if not bret & QMessageBox.Ok: return updateRestShape(mesh, sel)
def renameFalloff(self): if not self.simplex.falloffs: return idx = self.uiShapeFalloffCBOX.currentIndex() if idx < 0: return fo = self.simplex.falloffs[idx] foNames = [f.name for f in self.simplex.falloffs] foNames.pop(idx) newName, good = QInputDialog.getText( self, "Rename Falloff", "Enter a new name for the Falloff", text=fo.name) if not good: return if not NAME_CHECK.match(newName): message = 'Falloff name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return nn = getNextName(newName, foNames) fo.name = nn
def newComboGroup(self): if self.simplex is None: return newName, good = QInputDialog.getText(self, "New Group", "Enter a name for the new group", text="Group") if not good: return if not NAME_CHECK.match(newName): message = 'Group name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return Group.createGroup(str(newName), self.simplex, groupType=Combo) self.uiComboTREE.model().sourceModel().invalidateFilter()
def exportSplitInterface(window): if np is None: QMessageBox.warning(window, "No Numpy", "Numpy is not available here, an it is required to split a system") return path, _filter = QtCompat.QFileDialog.getSaveFileName(window, "Export Split", "", "Simplex (*.smpx)") if not path: return pBar = QProgressDialog("Exporting Split smpx File", "Cancel", 0, 100, window) pBar.show() split = window.simplex.split(pBar) split.exportAbc(path, pBar) pBar.close()
def setShapeName(self): progPairs = self.uiSliderTREE.getSelectedItems(ProgPair) if len(progPairs) != 1: message = 'You can set exactly one shape name at a time this way' QMessageBox.warning(self, 'Warning', message) return newName = self.uiShapeNameTXT.text() if not NAME_CHECK.match(newName): message = 'Slider name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return progPairs[0].shape.name = newName self.uiSliderTREE.viewport().update()
def sliderTreeDelete(self): idxs = self.uiSliderTREE.getSelectedIndexes() roots = coerceIndexToRoots(idxs) if not roots: QMessageBox.warning(self, 'Warning', 'Nothing Selected') return roots = makeUnique([i.model().itemFromIndex(i) for i in roots]) for r in roots: if isinstance(r, Simplex): QMessageBox.warning(self, 'Warning', 'Cannot delete a simplex system this way (for now)') return for r in roots: r.delete() self.uiSliderTREE.model().invalidateFilter()
def generateShapeIncrementalsContext(indexes, window): idx = indexes[0] # Only on the click index slider = idx.model().itemFromIndex(idx) if len(slider.prog.pairs) > 2: QMessageBox.warning(window, "Warning", "Slider already has incrementals") return increments, good = QInputDialog.getInt(window, "Increments", "Number of Increments", 4, 1, 100) if not good: return rest = None target = None maxval = -1.0 for pp in slider.prog.pairs: if pp.shape.isRest: rest = pp.shape elif abs(pp.value) > maxval: target = pp.shape target.name = target.name + "_100" startObj = slider.extractShape(rest, live=False) endObj = slider.extractShape(target) shapeDup = cmds.duplicate(endObj, name="shapeDup")[0] bs = cmds.blendShape(startObj, shapeDup) incs = [] for i in range(1, increments): val = float(increments - i) / increments percent = int(float(i) * 100 / increments) cmds.blendShape(bs, edit=True, weight=((0, val))) nne = endObj.replace("_100_", "_{0}_".format(percent)) nn = nne.replace("_Extract", "") inc = cmds.duplicate(shapeDup, name=nne) incs.append((percent, nn, nne)) for perc, inc, ince in incs: pp = slider.createShape(shapeName=inc, tVal=perc/100.0) pp.shape.connectShape(mesh=ince, live=True) window.uiSliderTREE.model().invalidateFilter() cmds.delete((shapeDup, startObj)) cmds.select(endObj)
def newGroup(self): if self.simplex is None: return newName, good = QInputDialog.getText(self, "New Group", "Enter a name for the new group", text="Group") if not good: return if not NAME_CHECK.match(newName): message = 'Group name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return items = self.uiTraversalTREE.getSelectedItems(Slider) Group.createGroup(str(newName), self.simplex, items)
def newSlider(self): if self.simplex is None: return # get the new slider name newName, good = QInputDialog.getText(self, "New Slider", "Enter a name for the new slider", text="Slider") if not good: return if not NAME_CHECK.match(newName): message = 'Slider name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return idxs = self.uiSliderTREE.getSelectedIndexes() groups = coerceIndexToParentType(idxs, Group) group = groups[0].model().itemFromIndex(groups[0]) if groups else None Slider.createSlider(str(newName), self.simplex, group=group)
def renameSystem(self): if self.simplex is None: return nn, good = QInputDialog.getText(self, "New System Name", "Enter a name for the System", text=self.simplex.name) if not good: return if not NAME_CHECK.match(nn): message = 'System name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return sysNames = [str(self.uiCurrentSystemCBOX.itemText(i)) for i in range(self.uiCurrentSystemCBOX.count())] nn = getNextName(nn, sysNames) self.simplex.name = nn idx = self.uiCurrentSystemCBOX.currentIndex() self.uiCurrentSystemCBOX.setItemText(idx, nn) self.currentSystemChanged(idx)
def newSystem(self): if self._currentObject is None: QMessageBox.warning(self, 'Warning', 'Must have a current object selection') return newName, good = QInputDialog.getText(self, "New System", "Enter a name for the new system") if not good: return newName = str(newName) if not NAME_CHECK.match(newName): message = 'System name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return newSystem = Simplex.buildEmptySystem(self._currentObject, newName, sliderMul=self._sliderMul) with signalsBlocked(self.uiCurrentSystemCBOX): self.uiCurrentSystemCBOX.addItem(newName) self.uiCurrentSystemCBOX.setCurrentIndex(self.uiCurrentSystemCBOX.count()-1) self.setSystem(newSystem)
def newFalloff(self): if not self.simplex.falloffs: return foNames = [f.name for f in self.simplex.falloffs] tempName = getNextName("NewFalloff", foNames) newName, good = QInputDialog.getText( self, "Rename Falloff", "Enter a new name for the Falloff", text=tempName) if not good: return if not NAME_CHECK.match(newName): message = 'Falloff name can only contain letters and numbers, and cannot start with a number' QMessageBox.warning(self, 'Warning', message) return nn = getNextName(newName, foNames) Falloff.createPlanar(nn, self.simplex, 'X', 1.0, 0.66, 0.33, -1.0)
def newTrav(self): sliders = self.parent().uiSliderTREE.getSelectedItems(Slider) combos = self.parent().uiComboTREE.getSelectedItems(Combo) items = sliders + combos if len(items) != 2: message = 'Must have exactly 2 other controller selected' QMessageBox.warning(self, 'Warning', message) return None # the progressor will have more shapes in the prog val0 = items[0].prog.getValues() val1 = items[1].prog.getValues() pos0count = len([i for i in val0 if i > 0]) neg0count = len([i for i in val0 if i < 0]) pos1count = len([i for i in val1 if i > 0]) neg1count = len([i for i in val1 if i < 0]) # Find the prog item vals = [pos0count, neg0count, pos1count, neg1count] mIdx = vals.index(max(vals)) iidx = 0 if mIdx < 2 else 1 progItem = items[iidx] multItem = items[iidx - 1] progFlip = (mIdx % 2) == 1 multFlip = False name = Traversal.buildTraversalName(progItem, multItem, progFlip, multFlip) return Traversal.createTraversal(name, self.simplex, multItem, progItem, multFlip, progFlip, count=vals[mIdx])
def importObjFolder(self, folder): ''' Import all objs from a user selected folder ''' if not os.path.isdir(folder): QMessageBox.warning(self, 'Warning', 'Folder does not exist') return paths = os.listdir(folder) paths = [i for i in paths if i.endswith('.obj')] if not paths: QMessageBox.warning(self, 'Warning', 'Folder does not contain any .obj files') return shapeDict = {shape.name: shape for shape in self.simplex.shapes} inPairs = {} for path in paths: shapeName = os.path.splitext(os.path.basename(path))[0] shape = shapeDict.get(shapeName) if shape is not None: inPairs[shapeName] = path else: sfx = "_Extract" if shapeName.endswith(sfx): shapeName = shapeName[:-len(sfx)] shape = shapeDict.get(shapeName) if shape is not None: inPairs[shapeName] = path sliderMasters, comboMasters = {}, {} for masters in [self.simplex.sliders, self.simplex.combos]: for master in masters: for pp in master.prog.pairs: shape = shapeDict.get(pp.shape.name) if shape is not None: if shape.name in inPairs: if isinstance(master, Slider): sliderMasters[shape.name] = master if isinstance(master, Combo): comboMasters[shape.name] = master comboDepth = {} for k, v in comboMasters.iteritems(): depth = len(v.pairs) comboDepth.setdefault(depth, {})[k] = v pBar = QProgressDialog("Loading from Mesh", "Cancel", 0, len(comboMasters) + len(sliderMasters), self) pBar.show() for shapeName, slider in sliderMasters.iteritems(): pBar.setValue(pBar.value() + 1) pBar.setLabelText("Loading Obj :\n{0}".format(shapeName)) QApplication.processEvents() if pBar.wasCanceled(): return path = inPairs[shapeName] mesh = self.simplex.DCC.importObj(os.path.join(folder, path)) shape = shapeDict[shapeName] self.simplex.DCC.connectShape(shape, mesh=mesh, live=False, delete=True) for depth in sorted(comboDepth.keys()): for shapeName, combo in comboDepth[depth].iteritems(): pBar.setValue(pBar.value() + 1) pBar.setLabelText("Loading Obj :\n{0}".format(shapeName)) QApplication.processEvents() if pBar.wasCanceled(): return path = inPairs[shapeName] mesh = self.simplex.DCC.importObj(os.path.join(folder, path)) shape = shapeDict[shapeName] self.simplex.DCC.connectComboShape(combo, shape, mesh=mesh, live=False, delete=True) pBar.close()