class ActionTreeWindow(PulseWindow): """ A standalone window that contains an ActionTreeWidget and an ActionPaletteWidget. """ OBJECT_NAME = 'pulseActionTreeWindow' PREFERRED_SIZE = QtCore.QSize(400, 300) STARTING_SIZE = QtCore.QSize(400, 300) MINIMUM_SIZE = QtCore.QSize(400, 300) WINDOW_MODULE = 'pulse.views.actiontree' def __init__(self, parent=None): super(ActionTreeWindow, self).__init__(parent=parent) self.setWindowTitle('Pulse Action Tree') layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) self.actionTree = ActionTreeWidget(self) layout.addWidget(self.actionTree) self.actionPalette = ActionPaletteWidget(self) layout.addWidget(self.actionPalette) layout.setStretch(layout.indexOf(self.actionTree), 2) layout.setStretch(layout.indexOf(self.actionPalette), 1)
def parent(self, index): if not index.isValid(): return QtCore.QModelIndex() thisStep = self.stepForIndex(index) parentStep = thisStep.parent if thisStep else None if not parentStep or parentStep == self._blueprint.rootStep: return QtCore.QModelIndex() return self.createIndex(parentStep.indexInParent(), 0, parentStep)
def index(self, row, column, parent): # override if not self.hasIndex(row, column, parent): return QtCore.QModelIndex() parentItem = self.getItem(parent) childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QtCore.QModelIndex()
def parent(self, index): # override if not index.isValid(): return QtCore.QModelIndex() childItem = index.internalPointer() parentItem = childItem.parent() if parentItem == self.rootItem: return QtCore.QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem)
def index(self, row, column, parent=QtCore.QModelIndex()): """ Create a QModelIndex for a row, column, and parent index """ if parent.isValid() and column != 0: return QtCore.QModelIndex() parentStep = self.stepForIndex(parent) if parentStep and parentStep.canHaveChildren: step = parentStep.getChildAt(row) return self.createIndex(row, column, step) return QtCore.QModelIndex()
def parent(self, index): if not index.isValid(): return QtCore.QModelIndex() childStep = self.stepForIndex(index) if childStep: parentStep = childStep.parent else: return QtCore.QModelIndex() if parentStep is None or parentStep == self._blueprint.rootStep: return QtCore.QModelIndex() return self.createIndex(parentStep.indexInParent(), 0, parentStep)
def __init__(self, index, parent=None): super(BuildActionProxyForm, self).__init__(parent=parent) self.index = QtCore.QPersistentModelIndex(index) self.hasVariantsUi = False self.setupUi(self) self.updateVariantFormList() self.index.model().dataChanged.connect(self.onModelDataChanged)
def createAttrForm(self, actionData, attr, parent): isVariant = False # duck type of actionProxy if hasattr(actionData, 'isVariantAttr'): isVariant = actionData.isVariantAttr(attr['name']) if isVariant: attrForm = BatchAttrForm.createForm(self.index, attr, parent=parent) else: attrForm = ActionAttrForm.createForm(self.index, attr, self.variantIndex, parent=parent) attrForm.isBatchForm = isVariant # add toggle variant button to label layout toggleVariantBtn = QtWidgets.QPushButton(parent) toggleVariantBtn.setCheckable(True) toggleVariantBtn.setText("ยท") toggleVariantBtn.setFixedSize(QtCore.QSize(14, 20)) attrForm.labelLayout.insertWidget(0, toggleVariantBtn) attrForm.labelLayout.setAlignment(toggleVariantBtn, QtCore.Qt.AlignTop) toggleVariantBtn.clicked.connect( partial(self.toggleIsVariantAttr, attr['name'])) attrForm.toggleVariantBtn = toggleVariantBtn return attrForm
def setupUi(self, parent): self.setupDefaultFormUi(parent) hlayout = QtWidgets.QHBoxLayout(parent) hlayout.setSpacing(4) self.listWidget = QtWidgets.QListWidget(parent) self.listWidget.setSortingEnabled(True) self.listWidget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) self.listWidget.setSelectionMode( QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) self.listWidget.itemSelectionChanged.connect( self.onItemSelectionChanged) hlayout.addWidget(self.listWidget) self.pickButton = QtWidgets.QPushButton(parent) self.pickButton.setIcon(viewutils.getIcon("select.png")) self.pickButton.setFixedSize(QtCore.QSize(20, 20)) self.pickButton.clicked.connect(self.setFromSelection) hlayout.addWidget(self.pickButton) hlayout.setAlignment(self.pickButton, QtCore.Qt.AlignTop) self.setDefaultFormLayout(hlayout) if self._isValueTypeValid(self.attrValue): self._setFormValue(self.attrValue)
def setupVariantsUi(self, parent, layout): # variant header variantHeader = QtWidgets.QFrame(parent) variantHeader.setStyleSheet( ".QFrame{ background-color: rgb(255, 255, 255, 15); border-radius: 2px }" ) layout.addWidget(variantHeader) variantHeaderLayout = QtWidgets.QHBoxLayout(variantHeader) variantHeaderLayout.setContentsMargins(10, 4, 4, 4) variantHeaderLayout.setSpacing(4) self.variantsLabel = QtWidgets.QLabel(variantHeader) self.variantsLabel.setText("Variants: ") variantHeaderLayout.addWidget(self.variantsLabel) spacer = QtWidgets.QSpacerItem(20, 4, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) layout.addItem(spacer) # add variant button addVariantBtn = QtWidgets.QPushButton(variantHeader) addVariantBtn.setText('+') addVariantBtn.setFixedSize(QtCore.QSize(20, 20)) addVariantBtn.clicked.connect(self.addVariant) variantHeaderLayout.addWidget(addVariantBtn) # variant list layout self.variantListLayout = QtWidgets.QVBoxLayout(parent) self.variantListLayout.setContentsMargins(0, 0, 0, 0) self.variantListLayout.setSpacing(4) layout.addLayout(self.variantListLayout)
def insertRows(self, row, count, parent=QtCore.QModelIndex()): self.beginInsertRows(parent, row, row + count - 1) step = self.stepForIndex(parent) for _ in range(count): step.insertChild(row, BuildStep()) self.endInsertRows() return True
def data(self, column, role=QtCore.Qt.DisplayRole): if role == QtCore.Qt.DisplayRole: if isinstance(self.buildItem, pulse.BuildGroup): return '{0} ({1})'.format(self.buildItem.getDisplayName(), self.buildItem.getChildCount()) elif isinstance(self.buildItem, pulse.BatchBuildAction): return '{0} (x{1})'.format(self.buildItem.getDisplayName(), self.buildItem.getActionCount()) else: return self.buildItem.getDisplayName() elif role == QtCore.Qt.EditRole: return self.buildItem.getDisplayName() elif role == QtCore.Qt.DecorationRole: iconFile = self.buildItem.getIconFile() if iconFile: return QtGui.QIcon(iconFile) elif role == QtCore.Qt.SizeHintRole: return QtCore.QSize(0, 20) elif role == QtCore.Qt.ForegroundRole: color = self.buildItem.getColor() if color: return QtGui.QColor(*[c * 255 for c in color])
class CollapsibleFrame(QtWidgets.QFrame): """ A QFrame that can be collapsed when clicked. """ collapsedChanged = QtCore.Signal(bool) def __init__(self, parent): super(CollapsibleFrame, self).__init__(parent) self._isCollapsed = False def mouseReleaseEvent(self, QMouseEvent): if QMouseEvent.button() == QtCore.Qt.MouseButton.LeftButton: self.setIsCollapsed(not self._isCollapsed) else: return super(CollapsibleFrame, self).mouseReleaseEvent(QMouseEvent) def setIsCollapsed(self, newCollapsed): """ Set the collapsed state of this frame. """ self._isCollapsed = newCollapsed self.collapsedChanged.emit(self._isCollapsed) def isCollapsed(self): """ Return True if the frame is currently collapsed. """ return self._isCollapsed
def createStepsForSelection(self): """ Create new BuildSteps in the hierarchy at the current selection and return the new model indexes. """ if self.blueprintModel.isReadOnly(): return selIndexes = self.selectionModel.selectedIndexes() if not selIndexes: selIndexes = [QtCore.QModelIndex()] model = self.selectionModel.model() def getParentAndInsertIndex(index): step = model.stepForIndex(index) print('step', step) if step.canHaveChildren: print('inserting at num children') return index, step.numChildren() else: print('inserting at selected + 1') return model.parent(index), index.row() + 1 newIndexes = [] for index in selIndexes: parentIndex, insertIndex = getParentAndInsertIndex(index) if self.model.insertRows(insertIndex, 1, parentIndex): newIndex = self.model.index(insertIndex, 0, parentIndex) newIndexes.append(newIndex) return newIndexes
class ActionForm(BuildItemForm): """ Form for editing Actions that displays an attr form for every attribute on the action. """ convertToBatchClicked = QtCore.Signal() def setupUi(self, parent): super(ActionForm, self).setupUi(parent) # add batch conversion button to header convertToBatchBtn = QtWidgets.QPushButton(parent) convertToBatchBtn.setIcon( viewutils.getIcon("convertActionToBatch.png")) convertToBatchBtn.setFixedSize(QtCore.QSize(18, 18)) convertToBatchBtn.clicked.connect(self.convertToBatchClicked.emit) self.headerLayout.addWidget(convertToBatchBtn) def setupContentUi(self, parent): for attr in self.buildItem.config['attrs']: attrValue = getattr(self.buildItem, attr['name']) attrForm = ActionAttrForm.createForm(attr, attrValue, parent=parent) attrForm.valueChanged.connect( partial(self.attrValueChanged, attrForm)) self.mainLayout.addWidget(attrForm) def attrValueChanged(self, attrForm, attrValue, isValueValid): setattr(self.buildItem, attrForm.attr['name'], attrValue) self.buildItemChanged.emit()
def setupDefaultFormUi(self, parent): """ Optional UI setup that builds a standardized layout. Includes a form layout and a label with the attributes name. Should be called at the start of setupUi if desired. """ self.formLayout = QtWidgets.QFormLayout(parent) # margin that will give us some visible area of # the frame that can change color based on valid state self.formLayout.setMargin(2) self.formLayout.setFieldGrowthPolicy( QtWidgets.QFormLayout.ExpandingFieldsGrow) self.formLayout.setLabelAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTop | QtCore.Qt.AlignTrailing) self.formLayout.setHorizontalSpacing(10) # attribute name self.labelLayout = QtWidgets.QHBoxLayout(parent) self.label = QtWidgets.QLabel(parent) self.label.setMinimumSize( QtCore.QSize(self.LABEL_WIDTH, self.LABEL_HEIGHT)) self.label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignTop) # add some space above the label so it lines up self.label.setMargin(2) self.label.setText(pulse.names.toTitle(self.attr['name'])) self.labelLayout.addWidget(self.label) self.formLayout.setLayout(0, QtWidgets.QFormLayout.LabelRole, self.labelLayout)
def data(self, index, role=QtCore.Qt.DisplayRole): if not index.isValid(): return step = self.stepForIndex(index) if not step: return if role == QtCore.Qt.DisplayRole: return step.getDisplayName() elif role == QtCore.Qt.EditRole: return step.name elif role == QtCore.Qt.DecorationRole: iconFile = step.getIconFile() if iconFile: return QtGui.QIcon(iconFile) elif role == QtCore.Qt.SizeHintRole: return QtCore.QSize(0, 20) elif role == QtCore.Qt.ForegroundRole: color = step.getColor() if color: return QtGui.QColor(*[c * 255 for c in color])
def mimeData(self, indexes): result = QtCore.QMimeData() itemDataList = [ index.internalPointer().buildItem.serialize() for index in indexes ] datastr = meta.encodeMetaData(itemDataList) result.setData('text/plain', datastr) return result
def indexByStepPath(self, path): """ Return a QModelIndex for a step by path """ if self._blueprint: step = self._blueprint.getStepByPath(path) return self.indexByStep(step) return QtCore.QModelIndex()
def removeRows(self, position, rows, parent=QtCore.QModelIndex()): parentItem = self.getItem(parent) self.beginRemoveRows(parent, position, position + rows - 1) success = parentItem.removeChildren(position, rows) self.endRemoveRows() return success
def __init__(self, index, parent=None): """ Args: index (QModelIndex): The index of the BuildStep """ super(BuildStepForm, self).__init__(parent=parent) self.index = QtCore.QPersistentModelIndex(index) self.setupUi(self) self.index.model().dataChanged.connect(self.onModelDataChanged)
def dropMimeData(self, data, action, row, column, parentIndex): if not self.canDropMimeData(data, action, row, column, parentIndex): return False if action == QtCore.Qt.IgnoreAction: return True try: stepDataList = meta.decodeMetaData(str(data.data('text/plain'))) except Exception as e: LOG.error(e) return False print('dropData', stepDataList, data, action, row, column, parentIndex) beginRow = 0 parentPath = None if parentIndex.isValid(): parentStep = self.stepForIndex(parentIndex) if parentStep: if parentStep.canHaveChildren: # drop into step group beginRow = parentStep.numChildren() parentPath = parentStep.getFullPath() else: # drop next to step beginRow = parentIndex.row() parentPath = os.path.dirname(parentStep.getFullPath()) if not parentPath: parentPath = '' beginRow = self.rowCount(QtCore.QModelIndex()) if row != -1: beginRow = row cmds.undoInfo(openChunk=True, chunkName='Drag Pulse Actions') self.isMoveActionOpen = True cmds.evalDeferred(self._deferredMoveUndoClose) # create dropped steps count = len(stepDataList) for i in range(count): stepDataStr = serializeAttrValue(stepDataList[i]) newStepPath = cmds.pulseCreateStep( parentPath, beginRow + i, stepDataStr) if newStepPath: newStepPath = newStepPath[0] if action == QtCore.Qt.MoveAction: # create queue of renames to perform after source # steps have been removed targetName = stepDataList[i].get('name', '') self.dragRenameQueue.append((newStepPath, targetName)) return True
def setupUi(self, parent): super(BatchActionForm, self).setupUi(parent) # add action conversion button to header convertToActionBtn = QtWidgets.QPushButton(parent) convertToActionBtn.setIcon( viewutils.getIcon("convertBatchToAction.png")) convertToActionBtn.setFixedSize(QtCore.QSize(18, 18)) convertToActionBtn.clicked.connect(self.convertToActionClicked.emit) self.headerLayout.addWidget(convertToActionBtn)
class BlueprintEditorWindow(PulseWindow): OBJECT_NAME = 'pulseBlueprintEditorWindow' PREFERRED_SIZE = QtCore.QSize(400, 300) STARTING_SIZE = QtCore.QSize(400, 300) MINIMUM_SIZE = QtCore.QSize(400, 300) WINDOW_MODULE = 'pulse.views.blueprinteditor' def __init__(self, parent=None): super(BlueprintEditorWindow, self).__init__(parent=parent) self.setWindowTitle('Pulse Blueprint Editor') layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) widget = BlueprintEditorWidget(self) layout.addWidget(widget)
class BatchAttrForm(QtWidgets.QWidget): """ The base class for an attribute form designed to bulk edit all variants of an attribute on a batch action. This appears where the default attr form usually appears when the attribute is marked as variant. BatchAttrForms should only exist if they provide an easy way to bulk set different values for all variants, as its pointless to provide functionality for setting all variants to the same value (would make the attribute constant). """ TYPEMAP = {} valuesChanged = QtCore.Signal() variantCountChanged = QtCore.Signal() @staticmethod def doesFormExist(attr): return attr['type'] in BatchAttrForm.TYPEMAP @staticmethod def createForm(action, attr, parent=None): """ Create a new ActionAttrForm of the appropriate type based on a BuildAction attribute. Args: attr: A dict representing the config of a BuildAction attribute """ attrType = attr['type'] if attrType in BatchAttrForm.TYPEMAP: return BatchAttrForm.TYPEMAP[attrType](action, attr, parent=parent) def __init__(self, batchAction, attr, parent=None): super(BatchAttrForm, self).__init__(parent=parent) self.batchAction = batchAction self.attr = attr self.setupUi(self) def setupUi(self, parent): raise NotImplementedError
class QuickNameWindow(PulseWindow): OBJECT_NAME = 'pulseQuickNameWindow' PREFERRED_SIZE = QtCore.QSize(400, 300) STARTING_SIZE = QtCore.QSize(400, 300) MINIMUM_SIZE = QtCore.QSize(400, 300) WINDOW_MODULE = 'pulse.views.quickname' def __init__(self, parent=None): super(QuickNameWindow, self).__init__(parent=parent) self.setWindowTitle('Quick Name Editor') layout = QtWidgets.QVBoxLayout(self) layout.setMargin(0) self.setLayout(layout) widget = QuickNameWidget(self) layout.addWidget(widget)
def createControlShapeButton(text, shapeData): btn = QtWidgets.QPushButton(parent) btn.setStatusTip("Create a new control") if 'icon' in shapeData: btn.setIcon(getIcon("controls/" + shapeData["icon"])) btn.setIconSize(QtCore.QSize(32, 32)) else: btn.setText(text) btn.clicked.connect( cmd(pulse.controlshapes.createControlsForSelected, shapeData)) return btn
class CopyPastMatrixWindow(PulseWindow): OBJECT_NAME = 'pulseCopyPasteMatrixWindow' PREFERRED_SIZE = QtCore.QSize(220, 160) STARTING_SIZE = QtCore.QSize(220, 160) MINIMUM_SIZE = QtCore.QSize(220, 160) REQUIRED_PLUGINS = [] WINDOW_MODULE = 'pulse.views.utilviews' def __init__(self, parent=None): super(CopyPastMatrixWindow, self).__init__(parent=parent) self.setWindowTitle('Copy Paste Matrix') layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) widget = CopyPastMatrixWidget(self) layout.addWidget(widget)
def __init__(self, index, variantIndex=-1, parent=None): super(BuildActionDataForm, self).__init__(parent=parent) self.index = QtCore.QPersistentModelIndex(index) self.variantIndex = variantIndex self.setupUi(self) # the map of all attr forms, indexed by attr name self._attrForms = {} self.updateAttrFormList() self.index.model().dataChanged.connect(self.onModelDataChanged)
def insertBuildItems(self, position, childBuildItems, parent=QtCore.QModelIndex()): parentItem = self.getItem(parent) self.beginInsertRows(parent, position, position + len(childBuildItems) - 1) success = parentItem.insertChildren(position, childBuildItems) self.endInsertRows() return success