def dragObject(self): if not self.selectedIndexes(): return item = self.selectedItems()[0] role = item.data(0, QtCore.Qt.UserRole) if role == 'Folder': return text = 'BeamComponent:' + role mimeData = QtCore.QMimeData() mimeData.setText(text) drag = QtGui.QDrag(self) drag.setMimeData(mimeData) drag.setHotSpot(QtCore.QPoint(90, 23)) ghostComponent = QtGui.QPixmap(180, 46) #ghostComponent.fill(QtGui.QColor(67, 143, 153, 80)) ghostComponent.fill (QtGui.QColor (255, 255, 255, 255)) drag.setPixmap(ghostComponent) drag.start(QtCore.Qt.IgnoreAction)
class KColorWidget(QtWidgets.QLabel): """Custom Label widget to display color settings.""" clicked = QtCore.Signal(bool) colorChanged = QtCore.Signal(QtGui.QColor) def __init__(self, parent, color): super(KColorWidget, self).__init__(parent) self.installEventFilter(self) self._color = QtGui.QColor(color) self.pixmap = QtGui.QPixmap(12, 12) self.pixmap.fill(self._color) self.setProperty('colorLabel', True) self.setFixedSize(24, 24) self.setScaledContents(True) self.setPixmap(self.pixmap) self.createConnections() def createConnections(self): self.clicked.connect(self.openColorDialog) self.colorChanged.connect(self.changeColor) def eventFilter(self, object, event): if event.type() == QtCore.QEvent.Enter: self.setCursor(QtCore.Qt.PointingHandCursor) return True if event.type() == QtCore.QEvent.Leave: self.setCursor(QtCore.Qt.ArrowCursor) return True if event.type() == QtCore.QEvent.MouseButtonPress: self.setCursor(QtCore.Qt.ClosedHandCursor) return True if event.type() == QtCore.QEvent.MouseButtonRelease: self.setCursor(QtCore.Qt.PointingHandCursor) self.clicked.emit(True) return True return False def openColorDialog(self): colorDialog = QtWidgets.QColorDialog() colorDialog.setOption(QtWidgets.QColorDialog.DontUseNativeDialog, True) newColor = colorDialog.getColor(self._color, self) if newColor.isValid(): self._color = newColor self.colorChanged.emit(newColor) def changeColor(self, color): self.pixmap.fill(color) self.setPixmap(self.pixmap)
def setDragPoint(self, dragPoint): topLeft = QtCore.QPointF(self.__mouseDownPos) bottomRight = QtCore.QPointF(dragPoint) if dragPoint.x() < self.__mouseDownPos.x(): topLeft.setX(dragPoint.x()) bottomRight.setX(self.__mouseDownPos.x()) if dragPoint.y() < self.__mouseDownPos.y(): topLeft.setY(dragPoint.y()) bottomRight.setY(self.__mouseDownPos.y()) self.setPos(topLeft) self.resize(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())
def drawBackground(self, painter, rect): oldTransform = painter.transform() painter.fillRect(rect, self._backgroundColor) left = int(rect.left()) - (int(rect.left()) % self._gridSizeFine) top = int(rect.top()) - (int(rect.top()) % self._gridSizeFine) # Draw horizontal fine lines gridLines = [] painter.setPen(self._gridPenS) y = float(top) while y < float(rect.bottom()): gridLines.append(QtCore.QLineF(rect.left(), y, rect.right(), y)) y += self._gridSizeFine painter.drawLines(gridLines) # Draw vertical fine lines gridLines = [] painter.setPen(self._gridPenS) x = float(left) while x < float(rect.right()): gridLines.append(QtCore.QLineF(x, rect.top(), x, rect.bottom())) x += self._gridSizeFine painter.drawLines(gridLines) # Draw thick grid left = int(rect.left()) - (int(rect.left()) % self._gridSizeCourse) top = int(rect.top()) - (int(rect.top()) % self._gridSizeCourse) # Draw vertical thick lines gridLines = [] painter.setPen(self._gridPenL) x = left while x < rect.right(): gridLines.append(QtCore.QLineF(x, rect.top(), x, rect.bottom())) x += self._gridSizeCourse painter.drawLines(gridLines) # Draw horizontal thick lines gridLines = [] painter.setPen(self._gridPenL) y = top while y < rect.bottom(): gridLines.append(QtCore.QLineF(rect.left(), y, rect.right(), y)) y += self._gridSizeCourse painter.drawLines(gridLines) return super(GraphView, self).drawBackground(painter, rect)
def paint(self, painter, option, widget): srcPoint = self.mapFromScene(self.__srcPortCircle.centerInSceneCoords()) dstPoint = self.mapFromScene(self.__dstPortCircle.centerInSceneCoords()) dist_between = dstPoint - srcPoint self.__path = QtGui.QPainterPath() self.__path.moveTo(srcPoint) self.__path.cubicTo( srcPoint + QtCore.QPointF(dist_between.x() * 0.4, 0), dstPoint - QtCore.QPointF(dist_between.x() * 0.4, 0), dstPoint ) self.setPath(self.__path) super(Connection, self).paint(painter, option, widget)
def getSelectedNodesCentroid(self): selectedNodes = self.getSelectedNodes() leftMostNode = None topMostNode = None for node in selectedNodes: nodePos = node.getGraphPos() if leftMostNode is None: leftMostNode = node else: if nodePos.x() < leftMostNode.getGraphPos().x(): leftMostNode = node if topMostNode is None: topMostNode = node else: if nodePos.y() < topMostNode.getGraphPos().y(): topMostNode = node xPos = leftMostNode.getGraphPos().x() yPos = topMostNode.getGraphPos().y() pos = QtCore.QPoint(xPos, yPos) return pos
class GraphView (QtWidgets.QGraphicsView): nodeAdded = QtCore.Signal (Node) def __init__ (self, parent = None): super (GraphView, self).__init__ (parent) self.setObjectName ('graphView') self.setRenderHint (QtGui.QPainter.Antialiasing) self.setRenderHint (QtGui.QPainter.TextAntialiasing) self.setHorizontalScrollBarPolicy (QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy (QtCore.Qt.ScrollBarAlwaysOff) size = QtCore.QSize (600, 400) self.resize (size) self.setSceneRect (-size.width () * 0.5, -size.height () * 0.5, size.width (), size.height ()) self.setAcceptDrops (True) self.reset() def reset(self): self.setScene(QtWidgets.QGraphicsScene()) def addNode (self, node, emitSignal = True): self.scene ().addItem (node) if emitSignal: self.nodeAdded.emit (node) return node
def __init__(self, port, graph, hOffset, color, connectionPointType): super(PortCircle, self).__init__(port) self.__port = port self._graph = graph self._connectionPointType = connectionPointType self.__connections = set() self._supportsOnlySingleConnections = connectionPointType == 'In' self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)) size = QtCore.QSizeF(self.__diameter, self.__diameter) self.setPreferredSize(size) self.setWindowFrameMargins(0, 0, 0, 0) self.transform().translate(self.__radius * hOffset, 0) self.__defaultPen = QtGui.QPen(QtGui.QColor(25, 25, 25), 1.0) self.__hoverPen = QtGui.QPen(QtGui.QColor(255, 255, 100), 1.5) self._ellipseItem = QtWidgets.QGraphicsEllipseItem(self) self._ellipseItem.setPen(self.__defaultPen) self._ellipseItem.setPos(size.width()/2, size.height()/2) self._ellipseItem.setRect( -self.__radius, -self.__radius, self.__diameter, self.__diameter, ) if connectionPointType == 'In': self._ellipseItem.setStartAngle(270 * 16) self._ellipseItem.setSpanAngle(180 * 16) self.setColor(color) self.setAcceptHoverEvents(True)
def __init__(self, graph, component): super(KNode, self).__init__(graph, component.getDecoratedName()) self.__component = component self.__inspectorWidget = None print "KNode.__component.getNumInputs(): %s"%(self.__component.getNumInputs()) for i in range(self.__component.getNumInputs()): componentInput = component.getInputByIndex(i) print "KNode.componentInput: %s"%(componentInput) self.addPort(KNodeInputPort(self, graph, componentInput)) print "KNode.__component.getNumOutputs(): %s" % (self.__component.getNumOutputs ()) for i in range(self.__component.getNumOutputs()): componentOutput = component.getOutputByIndex(i) self.addPort(KNodeOutputPort(self, graph, componentOutput)) self.setGraphPos( QtCore.QPointF( self.__component.getGraphPos().x, self.__component.getGraphPos().y ) ) #self.setGraphPos (QtCore.QPointF (1, 1)) nodeColor = component.getComponentColor() print "KNode.nodeColor: ",(nodeColor) self.setColor(QtGui.QColor(nodeColor[0], nodeColor[1], nodeColor[2], nodeColor[3])) self.setUnselectedColor(self.getColor().darker(125)) self.setSelectedColor(self.getColor().lighter(175))
class Node (QtWidgets.QGraphicsWidget): nameChanged = QtCore.Signal (str, str) __defaultColor = QtGui.QColor (154, 205, 50, 255) __defaultUnselectedColor = QtGui.QColor (25, 25, 25) __defaultSelectedColor = QtGui.QColor (255, 255, 255, 255) __defaultUnselectedPen = QtGui.QPen (__defaultUnselectedColor, 1.6) __defaultSelectedPen = QtGui.QPen (__defaultSelectedColor, 1.6) __defaultLinePen = QtGui.QPen (QtGui.QColor (25, 25, 25, 255), 1.25) def __init__ (self): super (Node, self).__init__ () self.__color = self.__defaultColor self.__unselectedColor = self.__defaultUnselectedColor self.__selectedColor = self.__defaultSelectedColor self.__unselectedPen = QtGui.QPen (self.__defaultUnselectedPen) self.__selectedPen = QtGui.QPen (self.__defaultSelectedPen) self.__linePen = QtGui.QPen (self.__defaultLinePen) self.setMinimumWidth (60) self.setMinimumHeight (20) self.setSizePolicy (QtWidgets.QSizePolicy (QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)) layout = QtWidgets.QGraphicsLinearLayout () layout.setContentsMargins (0, 0, 0, 0) layout.setSpacing (0) layout.setOrientation (QtCore.Qt.Vertical) self.setLayout (layout) self.__headerItem = NodeHeader ("xxx", self) layout.addItem (self.__headerItem) layout.setAlignment (self.__headerItem, QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) self.__ports = [] self.__inputPortsHolder = PortList (self) self.__ioPortsHolder = PortList (self) self.__outputPortsHolder = PortList (self) layout.addItem (self.__inputPortsHolder) layout.addItem (self.__ioPortsHolder) layout.addItem (self.__outputPortsHolder) self.__selected = False self.__dragging = False def addPort(self, port): if isinstance(port, InputPort): self.__inputPortsHolder.addPort(port, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) elif isinstance(port, OutputPort): self.__outputPortsHolder.addPort(port, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) else: self.__ioPortsHolder.addPort(port, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) self.__ports.append(port) self.adjustSize() return port
class componentMainSettings(QtWidgets.QDialog, guide.helperSlots): valueChanged = QtCore.Signal(int) def __init__(self, parent=None): super(componentMainSettings, self).__init__() # the inspectSettings function set the current selection to the # component root before open the settings dialog self.root = pm.selected()[0]
def boundingRect(self): srcPoint = self.mapFromScene(self.__srcPortCircle.centerInSceneCoords()) dstPoint = self.mapFromScene(self.__dstPortCircle.centerInSceneCoords()) penWidth = self.__defaultPen.width() return QtCore.QRectF( min(srcPoint.x(), dstPoint.x()), min(srcPoint.y(), dstPoint.y()), abs(dstPoint.x() - srcPoint.x()), abs(dstPoint.y() - srcPoint.y()), ).adjusted(-penWidth/2, -penWidth/2, +penWidth/2, +penWidth/2)
class RigNameLabel(QtWidgets.QLabel): clicked = QtCore.Signal() def __init__(self, parent=None): super(RigNameLabel, self).__init__(parent) self.setObjectName('rigNameLabel') self.setToolTip('Double Click to Edit') def mouseDoubleClickEvent(self, event): self.clicked.emit()
def __init__(self, parent=None): # constructors of base classes super(BeamUI, self).__init__(parent) self.setObjectName('mainUI') self.setWindowIcon(QtGui.QIcon('')) self.setWindowTitle("Beam Editor") self.setAcceptDrops(True) self.mainVboxLayout = QtGui.QVBoxLayout (self) self.guideLayout = QtGui.QVBoxLayout () self.mainVboxLayout.addLayout(self.guideLayout) self.build_from_selection = QtGui.QPushButton("build_from_selection") self.mainVboxLayout.addWidget(self.build_from_selection) compDir = shifter.getComponentDirectories () path = self.current_path () trackLoadComponent = [] for path, comps in compDir.iteritems (): for comp_name in comps: if comp_name in trackLoadComponent: pm.displayWarning( "Custom component name: %s, already in default " "components. Names should be unique. This component is" " not loaded" % comp_name) continue else: trackLoadComponent.append (comp_name) if not os.path.exists(os.path.join(path, comp_name, "__init__.py")): continue module = shifter.importComponentGuide (comp_name) print "BeamUI.__init__.module",module reload (module) commandbutton = self.loadUiWidget (os.path.join (path.replace("beam_components","widgets"), "commandbutton.ui")) icon = QtGui.QPixmap(os.path.join (path,module.TYPE,"icon.jpg")) self.guideLayout.addWidget (commandbutton) commandbutton.pushButton.setText(module.TYPE) commandbutton.label.setPixmap (icon) QtCore.QObject.connect (commandbutton.pushButton, QtCore.SIGNAL ("clicked()"), partial (self.drawComp, module.TYPE)) self.createConnections()
def __init__ (self, parent = None): super (GraphView, self).__init__ (parent) self.setObjectName ('graphView') self.setRenderHint (QtGui.QPainter.Antialiasing) self.setRenderHint (QtGui.QPainter.TextAntialiasing) self.setHorizontalScrollBarPolicy (QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy (QtCore.Qt.ScrollBarAlwaysOff) size = QtCore.QSize (600, 400) self.resize (size) self.setSceneRect (-size.width () * 0.5, -size.height () * 0.5, size.width (), size.height ()) self.setAcceptDrops (True) self.reset()
def frameNodes(self, nodes): if len(nodes) == 0: return def computeWindowFrame(): windowRect = self.rect() windowRect.setLeft(windowRect.left() + 16) windowRect.setRight(windowRect.right() - 16) windowRect.setTop(windowRect.top() + 16) windowRect.setBottom(windowRect.bottom() - 16) return windowRect nodesRect = None for node in nodes: nodeRectF = node.transform().mapRect(node.rect()) nodeRect = QtCore.QRect(nodeRectF.x(), nodeRectF.y(), nodeRectF.width(), nodeRectF.height()) if nodesRect is None: nodesRect = nodeRect else: nodesRect = nodesRect.united(nodeRect) windowRect = computeWindowFrame() scaleX = float(windowRect.width()) / float(nodesRect.width()) scaleY = float(windowRect.height()) / float(nodesRect.height()) if scaleY > scaleX: scale = scaleX else: scale = scaleY if scale < 1.0: self.setTransform(QtGui.QTransform.fromScale(scale, scale)) else: self.setTransform(QtGui.QTransform()) sceneRect = self.sceneRect() pan = sceneRect.center() - nodesRect.center() sceneRect.translate(-pan.x(), -pan.y()) self.setSceneRect(sceneRect) # Update the main panel when reframing. self.update()
def __init__(self, parent=None): super(GraphView, self).__init__(parent) self.setObjectName('graphView') self.__graphViewWidget = parent self.setRenderHint(QtGui.QPainter.Antialiasing) self.setRenderHint(QtGui.QPainter.TextAntialiasing) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) # Explicitly set the scene rect. This ensures all view parameters will be explicitly controlled # in the event handlers of this class. size = QtCore.QSize(600, 400) self.resize(size) self.setSceneRect(-size.width() * 0.5, -size.height() * 0.5, size.width(), size.height()) self.setAcceptDrops(True) self.reset()
def mouseMoveEvent(self, event): if self.__dragging: newPos = self.mapToScene(event.pos()) graph = self.getGraph() if graph.getSnapToGrid() is True: gridSize = graph.getGridSize() newNodePos = newPos - self._mouseDelta snapPosX = math.floor(newNodePos.x() / gridSize) * gridSize snapPosY = math.floor(newNodePos.y() / gridSize) * gridSize snapPos = QtCore.QPointF(snapPosX, snapPosY) newPosOffset = snapPos - newNodePos newPos = newPos + newPosOffset delta = newPos - self._lastDragPoint self.__graph.moveSelectedNodes(delta) self._lastDragPoint = newPos self._nodesMoved = True else: super(Node, self).mouseMoveEvent(event)
class GraphViewWidget(QtWidgets.QWidget): rigNameChanged = QtCore.Signal() def __init__(self, parent=None): # constructors of base classes super(GraphViewWidget, self).__init__(parent) self.openedFile = None self.setObjectName('graphViewWidget') self.setAttribute(QtCore.Qt.WA_WindowPropagation, True) def setGraphView(self, graphView): self.graphView = graphView # Setup Layout layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.graphView) self.setLayout(layout) ######################### ## Setup hotkeys for the following actions. deleteShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_Delete), self) deleteShortcut.activated.connect(self.graphView.deleteSelectedNodes) frameShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_F), self) frameShortcut.activated.connect(self.graphView.frameSelectedNodes) frameShortcut = QtWidgets.QShortcut( QtGui.QKeySequence(QtCore.Qt.Key_A), self) frameShortcut.activated.connect(self.graphView.frameAllNodes) def getGraphView(self): return self.graphView
def textSize(self): return QtCore.QSizeF( self.__fontMetrics.width(self.__text), self.__fontMetrics.height() )
class UndoRedoManager(QtCore.QObject): """An UndoManager manages the undo/redo stack for an application. Usually only a single undo manager is instantiated for a given application, but it is possible to instantiate multiple undomanagers, each one responsible for a separate undo stack. """ __instance = None undoUpdated = QtCore.Signal(object) def __init__(self): super(UndoRedoManager, self).__init__() self.__undoStack = [] self.__redoStack = [] self.__currentBracket = None self.__isUndoingOrRedoing = False self.__enabled = True self.__fireUpdateCallback() def enable(self): """Enables the UndoManager so that new brackets can be opened and commands added""" self.__enabled = True def disable(self): """Disables the UndoManager so that no new brackets can be opened or commands added""" self.__enabled = False def enabled(self): """Returns true if the UndoManager is enabled""" return self.__enabled def canAddCommand(self): """Returns True if the undo manager is in a state where a command can be added. A command can only be added if the undo manager is enabled, a bracket has been opened, and the undo manager is not currently undoing or redoing commands. """ return self.__enabled and not self.__isUndoingOrRedoing def isUndoingOrRedoing(self): """Returns true if the undo manager is currently undoing or redoing.""" return self.__isUndoingOrRedoing def addCommand(self, command, invokeRedoOnAdd=False): """ Adds a new command to the currently opened undo bracket. :param command: A command object which encapsulates the revertable action. """ if not self.canAddCommand(): raise Exception("Cannot add command when undo manager is disabled") if self.__currentBracket: self.__currentBracket.addCommand(command, invokeRedoOnAdd=invokeRedoOnAdd) else: if invokeRedoOnAdd: command.redo() if len(self.__undoStack) > 0: if self.__undoStack[len(self.__undoStack) - 1].mergeWith(command): # the command was merged with the previous, so does not need to be applied separately return self.__undoStack.append(command) self.__fireUpdateCallback() def openBracket(self, desc): """Opens a new undo bracket so that subsequent updo commands are added to the new bracket. When a command bracket it opened, all subsequent commands are added to the command bracket, which will be treated as a single undo on command the stack. openBracket can be called multiple times creating nested brackets. For each call to openBracket, closeBracket must also be called to ensure the undo manager is left in a valid state. :param desc: A string to describe the new bracket. This string can be used to populate undo widgets. """ if not self.canAddCommand(): raise Exception( "Cannot open bracket when undo manager is disabled") self.__currentBracket = CommandBracket(desc, self.__currentBracket) if not self.__currentBracket.getParentCommandBracket(): # Append the command if it is a root command bracket. self.__undoStack.append(self.__currentBracket) self.__clearRedoStack() # print ">>>openBracket:" + desc def closeBracket(self): """ Closes the currently open undo bracket, encapsulating all added commands into a single undable action. If multiple levels of brackets have been opened, the parent bracked is made the current active bracket. """ assert not self.__currentBracket is None, "UndoRedoManager.closeBracket() called but bracket has not been opened" # print "<<<closeBracket:" + self.__currentBracket.shortDesc() if self.__currentBracket.getNumCommands() == 0: print "Warning: UndoBracket closed with no commands added:" + self.__currentBracket.shortDesc( ) if self.__currentBracket.getParentCommandBracket() is not None: self.__currentBracket.getParentCommandBracket().popCommand() else: self.__undoStack.pop() # When a bracket is closed, the parent command backet is re-instated. self.__currentBracket = self.__currentBracket.getParentCommandBracket( ) else: self.__currentBracket.finalize() # When a bracket is closed, the parent command backet is re-instated. self.__currentBracket = self.__currentBracket.getParentCommandBracket( ) if not self.__currentBracket: # Fire the update only if the root level command bracket is closed. self.__fireUpdateCallback() # import inspect # for frame, filename, line_num, func, source_code, source_index in inspect.stack(): # print "stack :" + (filename) + ":" + str(func) + ":" + str(source_code) def cancelBracket(self): """ Cancels the currently open bracket, reverting all changes added to the bracket since openBracket was called. """ assert not self.__currentBracket is None, "UndoRedoManager.cancelBracket() called but bracket has not been opened" #print "<<<closeBracket:" + self.__currentBracket.shortDesc() self.closeBracket() self.undo() command = self.__redoStack.pop() command.destroy() self.__fireUpdateCallback() # def bracketOpened(self): """Returns True if a bracket has been opened. """ return not self.__currentBracket is None def haveUndo(self): """Returns Ture if the undo stack currently contains an undoable action""" return len(self.__undoStack) > 0 def canUndo(self): """Returns Ture if the undo stack currently contains an undoable action""" return self.haveUndo() def undo(self): """Reverts the action at the top of the undo stack""" assert self.haveUndo( ), "UndoRedoManager.undo() called but UndoRedoManager.haveUndo() is false" self.__isUndoingOrRedoing = True command = self.__undoStack.pop() command.undo() self.__redoStack.append(command) self.__fireUpdateCallback() self.__isUndoingOrRedoing = False def haveRedo(self): """Returns Ture if the redo stack currently contains an redoable action""" return len(self.__redoStack) > 0 def canRedo(self): """Returns Ture if the redo stack currently contains an redoable action""" return self.haveRedo() def redo(self): """Reapplies the action at the top of the redo stack""" self.__isUndoingOrRedoing = True command = self.__redoStack.pop() command.redo() self.__undoStack.append(command) self.__fireUpdateCallback() self.__isUndoingOrRedoing = False def __clearUndoStack(self): for command in self.__undoStack: command.destroy() self.__undoStack = [] def __clearRedoStack(self): for command in self.__redoStack: command.destroy() self.__redoStack = [] def reset(self): """Resets the undo manager, clearing both the undo and redo stacks""" self.__clearUndoStack() self.__clearRedoStack() self.__fireUpdateCallback() def destroy(self): """Destroys all data in the undo manager.""" self.reset() def __fireUpdateCallback(self): if self.haveUndo(): undoShortDesc = self.__undoStack[-1].shortDesc() else: undoShortDesc = None if self.haveRedo(): redoShortDesc = self.__redoStack[-1].shortDesc() else: redoShortDesc = None self.undoUpdated.emit({ 'undoShortDesc': undoShortDesc, 'canUndo': self.haveUndo(), 'redoShortDesc': redoShortDesc, 'canRedo': self.haveRedo() }) def logDebug(self): """Prints debug strings to help debug the state of the undo manager""" print "bracketOpened:" + str(self.bracketOpened()) print "undoStack:" for command in self.__undoStack: command.logDebug(1) print "redoStack:" for command in self.__redoStack: command.logDebug(1) print "-------------" @classmethod def getInstance(cls): if cls.__instance is None: cls.__instance = UndoRedoManager() return cls.__instance
class GraphView(QtWidgets.QGraphicsView): nodeAdded = QtCore.Signal(Node) nodeRemoved = QtCore.Signal(Node) nodeNameChanged = QtCore.Signal(str, str) beginDeleteSelection = QtCore.Signal() endDeleteSelection = QtCore.Signal() beginConnectionManipulation = QtCore.Signal() endConnectionManipulation = QtCore.Signal() connectionAdded = QtCore.Signal(Connection) connectionRemoved = QtCore.Signal(Connection) beginNodeSelection = QtCore.Signal() endNodeSelection = QtCore.Signal() selectionChanged = QtCore.Signal(list, list) # During the movement of the nodes, this signal is emitted with the incremental delta. selectionMoved = QtCore.Signal(set, QtCore.QPointF) # After moving the nodes interactively, this signal is emitted with the final delta. endSelectionMoved = QtCore.Signal(set, QtCore.QPointF) _clipboardData = None _backgroundColor = QtGui.QColor(50, 50, 50) _gridPenS = QtGui.QPen(QtGui.QColor(44, 44, 44, 255), 0.5) _gridPenL = QtGui.QPen(QtGui.QColor(40, 40, 40, 255), 1.0) _gridSizeFine = 30 _gridSizeCourse = 300 _mouseWheelZoomRate = 0.0005 _snapToGrid = False def __init__(self, parent=None): super(GraphView, self).__init__(parent) self.setObjectName('graphView') self.__graphViewWidget = parent self.setRenderHint(QtGui.QPainter.Antialiasing) self.setRenderHint(QtGui.QPainter.TextAntialiasing) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) # Explicitly set the scene rect. This ensures all view parameters will be explicitly controlled # in the event handlers of this class. size = QtCore.QSize(600, 400) self.resize(size) self.setSceneRect(-size.width() * 0.5, -size.height() * 0.5, size.width(), size.height()) self.setAcceptDrops(True) self.reset() def getGraphViewWidget(self): return self.__graphViewWidget ################################################ ## Graph def reset(self): self.setScene(QtWidgets.QGraphicsScene()) self.__connections = set() self.__nodes = {} self.__selection = set() self._manipulationMode = MANIP_MODE_NONE self._selectionRect = None def getGridSize(self): """Gets the size of the grid of the graph. Returns: int: Size of the grid. """ return self._gridSizeFine def getSnapToGrid(self): """Gets the snap to grid value. Returns: Boolean: Whether snap to grid is active or not. """ return self._snapToGrid def setSnapToGrid(self, snap): """Sets the snap to grid value. Args: snap (Boolean): True to snap to grid, false not to. """ self._snapToGrid = snap ################################################ ## Nodes def addNode(self, node, emitSignal=True): self.scene().addItem(node) self.__nodes[node.getName()] = node node.nameChanged.connect(self._onNodeNameChanged) if emitSignal: self.nodeAdded.emit(node) return node def removeNode(self, node, emitSignal=True): del self.__nodes[node.getName()] self.scene().removeItem(node) node.nameChanged.disconnect(self._onNodeNameChanged) if emitSignal: self.nodeRemoved.emit(node) def hasNode(self, name): return name in self.__nodes def getNode(self, name): if name in self.__nodes: return self.__nodes[name] return None def getNodes(self): return self.__nodes def _onNodeNameChanged(self, origName, newName): if newName in self.__nodes and self.__nodes[origName] != self.__nodes[ newName]: raise Exception("New name collides with existing node.") node = self.__nodes[origName] self.__nodes[newName] = node del self.__nodes[origName] self.nodeNameChanged.emit(origName, newName) def clearSelection(self, emitSignal=True): prevSelection = [] if emitSignal: for node in self.__selection: prevSelection.append(node) for node in self.__selection: node.setSelected(False) self.__selection.clear() if emitSignal and len(prevSelection) != 0: self.selectionChanged.emit(prevSelection, []) def selectNode(self, node, clearSelection=False, emitSignal=True): prevSelection = [] if emitSignal: for n in self.__selection: prevSelection.append(n) if clearSelection is True: self.clearSelection(emitSignal=False) if node in self.__selection: raise IndexError("Node is already in selection!") node.setSelected(True) self.__selection.add(node) if emitSignal: newSelection = [] for n in self.__selection: newSelection.append(n) self.selectionChanged.emit(prevSelection, newSelection) def deselectNode(self, node, emitSignal=True): if node not in self.__selection: raise IndexError("Node is not in selection!") prevSelection = [] if emitSignal: for n in self.__selection: prevSelection.append(n) node.setSelected(False) self.__selection.remove(node) if emitSignal: newSelection = [] for n in self.__selection: newSelection.append(n) self.selectionChanged.emit(prevSelection, newSelection) def getSelectedNodes(self): return self.__selection def deleteSelectedNodes(self): self.beginDeleteSelection.emit() selectedNodes = self.getSelectedNodes() names = "" for node in selectedNodes: node.disconnectAllPorts() self.removeNode(node) self.endDeleteSelection.emit() def frameNodes(self, nodes): if len(nodes) == 0: return def computeWindowFrame(): windowRect = self.rect() windowRect.setLeft(windowRect.left() + 16) windowRect.setRight(windowRect.right() - 16) windowRect.setTop(windowRect.top() + 16) windowRect.setBottom(windowRect.bottom() - 16) return windowRect nodesRect = None for node in nodes: nodeRectF = node.transform().mapRect(node.rect()) nodeRect = QtCore.QRect(nodeRectF.x(), nodeRectF.y(), nodeRectF.width(), nodeRectF.height()) if nodesRect is None: nodesRect = nodeRect else: nodesRect = nodesRect.united(nodeRect) windowRect = computeWindowFrame() scaleX = float(windowRect.width()) / float(nodesRect.width()) scaleY = float(windowRect.height()) / float(nodesRect.height()) if scaleY > scaleX: scale = scaleX else: scale = scaleY if scale < 1.0: self.setTransform(QtGui.QTransform.fromScale(scale, scale)) else: self.setTransform(QtGui.QTransform()) sceneRect = self.sceneRect() pan = sceneRect.center() - nodesRect.center() sceneRect.translate(-pan.x(), -pan.y()) self.setSceneRect(sceneRect) # Update the main panel when reframing. self.update() def frameSelectedNodes(self): self.frameNodes(self.getSelectedNodes()) def frameAllNodes(self): allnodes = [] for name, node in self.__nodes.iteritems(): allnodes.append(node) self.frameNodes(allnodes) def getSelectedNodesCentroid(self): selectedNodes = self.getSelectedNodes() leftMostNode = None topMostNode = None for node in selectedNodes: nodePos = node.getGraphPos() if leftMostNode is None: leftMostNode = node else: if nodePos.x() < leftMostNode.getGraphPos().x(): leftMostNode = node if topMostNode is None: topMostNode = node else: if nodePos.y() < topMostNode.getGraphPos().y(): topMostNode = node xPos = leftMostNode.getGraphPos().x() yPos = topMostNode.getGraphPos().y() pos = QtCore.QPoint(xPos, yPos) return pos def moveSelectedNodes(self, delta, emitSignal=True): for node in self.__selection: node.translate(delta.x(), delta.y()) if emitSignal: self.selectionMoved.emit(self.__selection, delta) # After moving the nodes interactively, this signal is emitted with the final delta. def endMoveSelectedNodes(self, delta): self.endSelectionMoved.emit(self.__selection, delta) ################################################ ## Events def mousePressEvent(self, event): if event.button() is QtCore.Qt.MouseButton.LeftButton and self.itemAt( event.pos()) is None: self.beginNodeSelection.emit() self._manipulationMode = MANIP_MODE_SELECT self._mouseDownSelection = copy.copy(self.getSelectedNodes()) self._selectionRect = SelectionRect(graph=self, mouseDownPos=self.mapToScene( event.pos())) elif event.button() is QtCore.Qt.MouseButton.MiddleButton: self.setCursor(QtCore.Qt.OpenHandCursor) self._manipulationMode = MANIP_MODE_PAN self._lastPanPoint = self.mapToScene(event.pos()) elif event.button() is QtCore.Qt.MouseButton.RightButton: self.setCursor(QtCore.Qt.SizeHorCursor) self._manipulationMode = MANIP_MODE_ZOOM self._lastZoomPoint = self.mapToScene(event.pos()) self._lastTransform = QtGui.QTransform(self.transform()) else: super(GraphView, self).mousePressEvent(event) def mouseMoveEvent(self, event): modifiers = QtWidgets.QApplication.keyboardModifiers() if self._manipulationMode == MANIP_MODE_SELECT: dragPoint = self.mapToScene(event.pos()) self._selectionRect.setDragPoint(dragPoint) # This logic allows users to use ctrl and shift with rectangle # select to add / remove nodes. if modifiers == QtCore.Qt.ControlModifier: for name, node in self.__nodes.iteritems(): if node in self._mouseDownSelection: if node.isSelected( ) and self._selectionRect.collidesWithItem(node): self.deselectNode(node, emitSignal=False) elif not node.isSelected( ) and not self._selectionRect.collidesWithItem(node): self.selectNode(node, emitSignal=False) else: if not node.isSelected( ) and self._selectionRect.collidesWithItem(node): self.selectNode(node, emitSignal=False) elif node.isSelected( ) and not self._selectionRect.collidesWithItem(node): if node not in self._mouseDownSelection: self.deselectNode(node, emitSignal=False) elif modifiers == QtCore.Qt.ShiftModifier: for name, node in self.__nodes.iteritems(): if not node.isSelected( ) and self._selectionRect.collidesWithItem(node): self.selectNode(node, emitSignal=False) elif node.isSelected( ) and not self._selectionRect.collidesWithItem(node): if node not in self._mouseDownSelection: self.deselectNode(node, emitSignal=False) else: self.clearSelection(emitSignal=False) for name, node in self.__nodes.iteritems(): if not node.isSelected( ) and self._selectionRect.collidesWithItem(node): self.selectNode(node, emitSignal=False) elif node.isSelected( ) and not self._selectionRect.collidesWithItem(node): self.deselectNode(node, emitSignal=False) elif self._manipulationMode == MANIP_MODE_PAN: delta = self.mapToScene(event.pos()) - self._lastPanPoint rect = self.sceneRect() rect.translate(-delta.x(), -delta.y()) self.setSceneRect(rect) self._lastPanPoint = self.mapToScene(event.pos()) elif self._manipulationMode == MANIP_MODE_MOVE: newPos = self.mapToScene(event.pos()) delta = newPos - self._lastDragPoint self._lastDragPoint = newPos selectedNodes = self.getSelectedNodes() # Apply the delta to each selected node for node in selectedNodes: node.translate(delta.x(), delta.y()) elif self._manipulationMode == MANIP_MODE_ZOOM: # How much delta = event.pos() - self._lastMousePos zoomFactor = 1.0 if delta.x() > 0: zoomFactor = 1.0 + delta.x() / 100.0 else: zoomFactor = 1.0 / (1.0 + abs(delta.x()) / 100.0) # Limit zoom to 3x if self._lastTransform.m22() * zoomFactor >= 2.0: return # Reset to when we mouse pressed self.setSceneRect(self._lastSceneRect) self.setTransform(self._lastTransform) # Center scene around mouse down rect = self.sceneRect() rect.translate(self._lastOffsetFromSceneCenter) self.setSceneRect(rect) # Zoom in (QGraphicsView auto-centers!) self.scale(zoomFactor, zoomFactor) newSceneCenter = self.sceneRect().center() newScenePos = self.mapToScene(self._lastMousePos) newOffsetFromSceneCenter = newScenePos - newSceneCenter # Put mouse down back where is was on screen rect = self.sceneRect() rect.translate(-1 * newOffsetFromSceneCenter) self.setSceneRect(rect) # Call udpate to redraw background self.update() else: super(GraphView, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self._manipulationMode == MANIP_MODE_SELECT: # If users simply clicks in the empty space, clear selection. if self.mapToScene(event.pos()) == self._selectionRect.pos(): self.clearSelection(emitSignal=False) self._selectionRect.destroy() self._selectionRect = None self._manipulationMode = MANIP_MODE_NONE selection = self.getSelectedNodes() deselectedNodes = [] selectedNodes = [] for node in self._mouseDownSelection: if node not in selection: deselectedNodes.append(node) for node in selection: if node not in self._mouseDownSelection: selectedNodes.append(node) if selectedNodes != deselectedNodes: self.selectionChanged.emit(deselectedNodes, selectedNodes) self.endNodeSelection.emit() elif self._manipulationMode == MANIP_MODE_PAN: self.setCursor(QtCore.Qt.ArrowCursor) self._manipulationMode = MANIP_MODE_NONE elif self._manipulationMode == MANIP_MODE_ZOOM: self.setCursor(QtCore.Qt.ArrowCursor) self._manipulationMode = MANIP_MODE_NONE #self.setTransformationAnchor(self._lastAnchor) else: super(GraphView, self).mouseReleaseEvent(event) def wheelEvent(self, event): zoomFactor = 1.0 + event.delta() * self._mouseWheelZoomRate transform = self.transform() # Limit zoom to 3x if transform.m22() * zoomFactor >= 2.0: return sceneCenter = self.sceneRect().center() scenePoint = self.mapToScene(event.pos()) posFromSceneCenter = scenePoint - sceneCenter rect = self.sceneRect() rect.translate(posFromSceneCenter) self.setSceneRect(rect) # Zoom in (QGraphicsView auto-centers!) self.scale(zoomFactor, zoomFactor) # Translate scene back to align original mouse press sceneCenter = self.sceneRect().center() scenePoint = self.mapToScene(event.pos()) posFromSceneCenter = scenePoint - sceneCenter rect = self.sceneRect() posFromSceneCenter *= -1.0 rect.translate(posFromSceneCenter) self.setSceneRect(rect) # Call udpate to redraw background self.update() # ============= # Copy / Paste # ============= def getClipboardData(self): return self.__class__._clipboardData
class ValueController(QtCore.QObject): valueChanged = QtCore.Signal(object) editableChanged = QtCore.Signal(bool) optionChanged = QtCore.Signal(str) def __init__(self, name, dataType, editable=True, **kwargs): super(ValueController, self).__init__() self.name = name self.dataType = dataType self.editable = editable self.options = kwargs def getName(self): return str(self.name) def getDataType(self): return self.dataType def setValue(self, value): command = ValueChangeCommand(self, value) UndoRedoManager.getInstance().addCommand(command, invokeRedoOnAdd=True) def _setValue_NoUndo(self, value): raise NotImplementedError() def getValue(self): raise NotImplementedError() def getOption(self, key, defaultValue=None): return self.options.get(key, defaultValue) def hasOption(self, key): return key in self.options def setOption(self, key, value): self.options[key] = value self.optionChanged.emit(key) def isEditable(self): return self.editable def setEditable(self, editable): self.editable = editable self.editableChanged.emit(editable) def _extractSimpleTypes(self, value): if str(type(value)) == "<type 'PyRTValObject'>" and ( self.dataType == 'Boolean' or \ self.dataType == 'UInt8' or \ self.dataType == 'Byte' or \ self.dataType == 'SInt8' or \ self.dataType == 'UInt16' or \ self.dataType == 'SInt16' or \ self.dataType == 'UInt32' or \ self.dataType == 'Count' or \ self.dataType == 'Index' or \ self.dataType == 'Size' or \ self.dataType == 'SInt32' or \ self.dataType == 'Integer' or \ self.dataType == 'UInt64' or \ self.dataType == 'DataSize' or \ self.dataType == 'SInt64' or \ self.dataType == 'Float32' or \ self.dataType == 'Scalar' or \ self.dataType == 'Float64' or \ self.dataType == 'String'): return value else: return value def emitValueChanged(self): self.valueChanged.emit(self.getValue())
class KGraphViewWidget(GraphViewWidget): rigNameChanged = QtCore.Signal() def __init__(self, parent=None): # constructors of base classes super(KGraphViewWidget, self).__init__(parent) self._builder = None self._guideBuilder = None self.guideRig = None graphView = KGraphView(parent=self) self.setGraphView(graphView) self.newRigPreset() def setGuideRigName(self, text): if text.endswith('_guide') is True: text = text.replace('_guide', '') self.guideRig.setName(text) self.rigNameChanged.emit() def newRigPreset(self): try: print "KGraphViewWidget.newRigPreset" self.guideRig = Rig() graphview = self.getGraphView() graphview.displayGraph(self.guideRig) self.setGuideRigName('MyRig') self.openedFile = None self.window().setWindowTitle('Beam Editor') print "newRigPreset,New Rig Created" #logger.inform("New Rig Created") except: print "newRigPreset,Error Creating New Rig" #logger.exception("Error Creating New Rig") def buildGuideRig(self): #try: self.window().setCursor(QtCore.Qt.WaitCursor) initConfigIndex = self.window().beamMenu.configsWidget.currentIndex() self.synchGuideRig() # Append "_guide" to rig name when building guide if self.guideRig.getName().endswith('_guide') is False: self.guideRig.setName(self.guideRig.getName() + '_guide') if self.window().preferences.getPreferenceValue( 'delete_existing_rigs'): if self._guideBuilder: self._guideBuilder.deleteBuildElements() self._guideBuilder = plugins.getBuilder() self._guideBuilder.build(self.guideRig) self.window().beamMenu.setCurrentConfig(initConfigIndex) #except: # pass #finally: print "buildGuideRig.buildGuideRig" self.window().setCursor(QtCore.Qt.ArrowCursor) def buildRig(self): print "buildRig" def synchGuideRig(self): synchronizer = plugins.getSynchronizer() # Guide is always built with "_guide" need this so synchronizer not confused with real Rig nodes if self.guideRig.getName().endswith('_guide') is False: self.guideRig.setName(self.guideRig.getName() + '_guide') synchronizer.setTarget(self.guideRig) synchronizer.sync() print "synchGuideRig.synchronizer: ", synchronizer # ============== # Other Methods # ============== def addBackdrop(self, name='Backdrop'): graphView = self.getGraphView() initName = name suffix = 1 collision = True while collision: collision = graphView.hasNode(name) if not collision: break result = re.split(r"(\d+)$", initName, 1) if len(result) > 1: initName = result[0] suffix = int(result[1]) name = initName + str(suffix).zfill(2) suffix += 1 backdropNode = KBackdrop(graphView, name) graphView.addNode(backdropNode) graphView.selectNode(backdropNode, clearSelection=True) return backdropNode
def textSize(self): return QtCore.QSizeF( self.__textItem.textWidth(), self.__font.pointSizeF() + self.__labelBottomSpacing)
class KGraphView(GraphView): beginCopyData = QtCore.Signal() endCopyData = QtCore.Signal() beginPasteData = QtCore.Signal() endPasteData = QtCore.Signal() _clipboardData = None def __init__(self, parent=None): super(KGraphView, self).__init__(parent) self.__rig = None self.setAcceptDrops(True) def getRig(self): return self.__rig # ====== # Graph # ====== def displayGraph(self, rig): print "KGraphView.displayGraph" self.reset() self.__rig = rig guideComponents = self.__rig.getChildrenByType('Component') for component in guideComponents: node = KNode(self, component) self.addNode(node) # ======= # Events # ======= def mousePressEvent(self, event): print "KGraphView: mousePressEvent" modifiers = QtWidgets.QApplication.keyboardModifiers() if event.button() == QtCore.Qt.MouseButton.RightButton: print "KGraphView: mousePressEvent.RightButton" zoom_with_alt_rmb = self.window().preferences.getPreferenceValue( 'zoom_with_alt_rmb') if zoom_with_alt_rmb and modifiers == QtCore.Qt.AltModifier: self._manipulationMode = MANIP_MODE_ZOOM self.setCursor(QtCore.Qt.SizeHorCursor) self._lastMousePos = event.pos() self._lastTransform = QtGui.QTransform(self.transform()) self._lastSceneRect = self.sceneRect() self._lastSceneCenter = self._lastSceneRect.center() self._lastScenePos = self.mapToScene(event.pos()) self._lastOffsetFromSceneCenter = self._lastScenePos - self._lastSceneCenter return def graphItemAt(item): if isinstance(item, KNode): return item if isinstance(item, Connection): return item elif item is not None: return graphItemAt(item.parentItem()) return None graphicItem = graphItemAt(self.itemAt(event.pos())) pos = self.mapToScene(event.pos()) if graphicItem is None: contextMenu = QtWidgets.QMenu(self.getGraphViewWidget()) contextMenu.setObjectName('rightClickContextMenu') contextMenu.setMinimumWidth(150) if self.getClipboardData() is not None: def pasteSettings(): self.pasteSettings(pos) def pasteSettingsMirrored(): self.pasteSettings(pos, mirrored=True) contextMenu.addAction("Paste").triggered.connect( pasteSettings) contextMenu.addAction("Paste Mirrored").triggered.connect( pasteSettingsMirrored) contextMenu.addSeparator() graphViewWidget = self.getGraphViewWidget() contextMenu.addAction("Add Backdrop").triggered.connect( graphViewWidget.addBackdrop) contextMenu.popup(event.globalPos()) if isinstance(graphicItem, KNode): self.selectNode(graphicItem, clearSelection=True, emitSignal=True) contextMenu = QtWidgets.QMenu(self.getGraphViewWidget()) contextMenu.setObjectName('rightClickContextMenu') contextMenu.setMinimumWidth(150) def copySettings(): self.copySettings(pos) contextMenu.addAction("Copy").triggered.connect(copySettings) if self.getClipboardData() is not None: def pasteSettings(): # Paste the settings, not modifying the location, because that will be used to determine symmetry. pasteData = self.getClipboardData()['components'][0] pasteData.pop('graphPos', None) graphicItem.getComponent().pasteData(pasteData, setLocation=False) contextMenu.addSeparator() contextMenu.addAction("Paste Data").triggered.connect( pasteSettings) contextMenu.popup(event.globalPos()) elif isinstance(graphicItem, Connection): outPort = graphicItem.getSrcPortCircle().getPort() inPort = graphicItem.getDstPortCircle().getPort() if outPort.getDataType() != inPort.getDataType(): if outPort.getDataType().startswith(inPort.getDataType( )) and outPort.getDataType().endswith('[]'): globalPos = event.globalPos() contextMenu = QtWidgets.QMenu( self.getGraphViewWidget()) contextMenu.setObjectName('rightClickContextMenu') contextMenu.setMinimumWidth(150) def editIndex(): componentInput = graphicItem.getDstPortCircle( ).getPort().getComponentInput() EditIndexWidget(componentInput, pos=globalPos, parent=self.getGraphViewWidget()) contextMenu.addAction("EditIndex").triggered.connect( editIndex) contextMenu.popup(globalPos) elif event.button( ) is QtCore.Qt.MouseButton.LeftButton and self.itemAt( event.pos()) is None: print "KGraphView: mousePressEvent.LeftButton" self.beginNodeSelection.emit() self._manipulationMode = MANIP_MODE_SELECT self._mouseDownSelection = copy.copy(self.getSelectedNodes()) self._selectionRect = SelectionRect(graph=self, mouseDownPos=self.mapToScene( event.pos())) elif event.button() is QtCore.Qt.MouseButton.MiddleButton: print "KGraphView: mousePressEvent.MiddleButton" pan_with_alt = self.window().preferences.getPreferenceValue( 'pan_with_alt') if pan_with_alt is True and modifiers != QtCore.Qt.AltModifier: return self.setCursor(QtCore.Qt.OpenHandCursor) self._manipulationMode = MANIP_MODE_PAN self._lastPanPoint = self.mapToScene(event.pos()) else: super(GraphView, self).mousePressEvent(event) def dragEnterEvent(self, event): print "KGraphView: dragEnterEvent" textParts = event.mimeData().text().split(':') if textParts[0] == 'BeamComponent': event.accept() else: event.setDropAction(QtCore.Qt.IgnoreAction) super(GraphView, self).dragEnterEvent(event) def dragMoveEvent(self, event): print "KGraphView: dragMoveEvent" super(GraphView, self).dragMoveEvent(event) event.accept() def dropEvent(self, event): print "KGraphView: dropEvent" textParts = event.mimeData().text().split(':') print "dropEvent.textParts :%s" % (textParts) if textParts[0] == 'BeamComponent': componentClassName = textParts[1] # Add a component to the rig placed at the given position. dropPosition = self.mapToScene(event.pos()) # construct the node and add it to the graph. beamSystem = BeamSystem.getInstance() componentClass = beamSystem.getComponentClass(componentClassName) component = componentClass(parent=self.getRig()) component.setGraphPos(Vec2(dropPosition.x(), dropPosition.y())) node = KNode(self, component) self.addNode(node) self.selectNode(node, clearSelection=True, emitSignal=False) event.acceptProposedAction() else: super(GraphView, self).dropEvent(event) def wheelEvent(self, event): print "KGraphView: wheelEvent" zoom_mouse_scroll = self.window().preferences.getPreferenceValue( 'zoom_mouse_scroll') if zoom_mouse_scroll is True: super(KGraphView, self).wheelEvent(event) # ============= # Copy / Paste # ============= def getClipboardData(self): return self.__class__._clipboardData def copySettings(self, pos): clipboardData = {} copiedComponents = [] nodes = self.getSelectedNodes() for node in nodes: copiedComponents.append(node.getComponent()) componentsJson = [] connectionsJson = [] for component in copiedComponents: componentsJson.append(component.copyData()) for i in range(component.getNumInputs()): componentInput = component.getInputByIndex(i) if componentInput.isConnected(): componentOutput = componentInput.getConnection() connectionJson = { 'source': componentOutput.getParent().getDecoratedName() + '.' + componentOutput.getName(), 'target': component.getDecoratedName() + '.' + componentInput.getName() } connectionsJson.append(connectionJson) clipboardData = { 'components': componentsJson, 'connections': connectionsJson, 'copyPos': pos } self.__class__._clipboardData = clipboardData def pasteSettings(self, pos, mirrored=False, createConnectionsToExistingNodes=True): clipboardData = self.__class__._clipboardData krakenSystem = BeamSystem.getInstance() delta = pos - clipboardData['copyPos'] self.clearSelection() pastedComponents = {} nameMapping = {} for componentData in clipboardData['components']: componentClass = krakenSystem.getComponentClass( componentData['class']) component = componentClass(parent=self.__rig) decoratedName = componentData[ 'name'] + component.getNameDecoration() nameMapping[decoratedName] = decoratedName if mirrored: config = Config.getInstance() mirrorMap = config.getNameTemplate()['mirrorMap'] component.setLocation(mirrorMap[componentData['location']]) nameMapping[decoratedName] = componentData[ 'name'] + component.getNameDecoration() component.pasteData(componentData, setLocation=False) else: component.pasteData(componentData, setLocation=True) graphPos = component.getGraphPos() component.setGraphPos( Vec2(graphPos.x + delta.x(), graphPos.y + delta.y())) node = KNode(self, component) self.addNode(node) self.selectNode(node, False) # save a dict of the nodes using the orignal names pastedComponents[nameMapping[decoratedName]] = component # Create Connections for connectionData in clipboardData['connections']: sourceComponentDecoratedName, outputName = connectionData[ 'source'].split('.') targetComponentDecoratedName, inputName = connectionData[ 'target'].split('.') sourceComponent = None # The connection is either between nodes that were pasted, or from pasted nodes # to unpasted nodes. We first check that the source component is in the pasted group # else use the node in the graph. if sourceComponentDecoratedName in nameMapping: sourceComponent = pastedComponents[ nameMapping[sourceComponentDecoratedName]] else: if not createConnectionsToExistingNodes: continue # When we support copying/pasting between rigs, then we may not find the source # node in the target rig. if not self.hasNode(sourceComponentDecoratedName): continue node = self.getNode(sourceComponentDecoratedName) sourceComponent = node.getComponent() targetComponentDecoratedName = nameMapping[ targetComponentDecoratedName] targetComponent = pastedComponents[targetComponentDecoratedName] outputPort = sourceComponent.getOutputByName(outputName) inputPort = targetComponent.getInputByName(inputName) inputPort.setConnection(outputPort) self.connectPorts(srcNode=sourceComponent.getDecoratedName(), outputName=outputPort.getName(), tgtNode=targetComponent.getDecoratedName(), inputName=inputPort.getName())
def minimumSizeHint(self): return QtCore.QSize(10, 25)
class Node(QtWidgets.QGraphicsWidget): nameChanged = QtCore.Signal(str, str) __defaultColor = QtGui.QColor(154, 205, 50, 255) __defaultUnselectedColor = QtGui.QColor(25, 25, 25) __defaultSelectedColor = QtGui.QColor(255, 255, 255, 255) __defaultUnselectedPen = QtGui.QPen(__defaultUnselectedColor, 1.6) __defaultSelectedPen = QtGui.QPen(__defaultSelectedColor, 1.6) __defaultLinePen = QtGui.QPen(QtGui.QColor(25, 25, 25, 255), 1.25) def __init__(self, graph, name): super(Node, self).__init__() self.__name = name self.__graph = graph self.__color = self.__defaultColor self.__unselectedColor = self.__defaultUnselectedColor self.__selectedColor = self.__defaultSelectedColor self.__unselectedPen = QtGui.QPen(self.__defaultUnselectedPen) self.__selectedPen = QtGui.QPen(self.__defaultSelectedPen) self.__linePen = QtGui.QPen(self.__defaultLinePen) self.setMinimumWidth(60) self.setMinimumHeight(20) self.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)) layout = QtWidgets.QGraphicsLinearLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.setOrientation(QtCore.Qt.Vertical) self.setLayout(layout) self.__headerItem = NodeHeader(self.__name, self) layout.addItem(self.__headerItem) layout.setAlignment(self.__headerItem, QtCore.Qt.AlignCenter | QtCore.Qt.AlignTop) self.__ports = [] self.__inputPortsHolder = PortList(self) self.__ioPortsHolder = PortList(self) self.__outputPortsHolder = PortList(self) layout.addItem(self.__inputPortsHolder) layout.addItem(self.__ioPortsHolder) layout.addItem(self.__outputPortsHolder) self.__selected = False self.__dragging = False # ===== # Name # ===== def getName(self): return self.__name def setName(self, name): if name != self.__name: origName = self.__name self.__name = name self.__headerItem.setText(self.__name) # Emit an event, so that the graph can update itsself. self.nameChanged.emit(origName, name) # Update the node so that the size is computed. self.adjustSize() # ======= # Colors # ======= def getColor(self): return self.__color def setColor(self, color): self.__color = color self.update() def getUnselectedColor(self): return self.__unselectedColor def setUnselectedColor(self, color): self.__unselectedColor = color self.__unselectedPen.setColor(self.__unselectedColor) self.update() def getSelectedColor(self): return self.__selectedColor def setSelectedColor(self, color): self.__selectedColor = color self.__selectedPen.setColor(self.__selectedColor) self.update() # ============= # Misc Methods # ============= def getGraph(self): return self.__graph def getHeader(self): return self.__headerItem # ========== # Selection # ========== def isSelected(self): return self.__selected def setSelected(self, selected=True): self.__selected = selected self.setZValue(20.0) self.update() ######################### ## Graph Pos def getGraphPos(self): transform = self.transform() size = self.size() return QtCore.QPointF(transform.dx() + (size.width() * 0.5), transform.dy() + (size.height() * 0.5)) def setGraphPos(self, graphPos): self.prepareConnectionGeometryChange() size = self.size() self.setTransform( QtGui.QTransform.fromTranslate( graphPos.x() - (size.width() * 0.5), graphPos.y() - (size.height() * 0.5)), False) def translate(self, x, y): self.prepareConnectionGeometryChange() currPos = self.pos() super(Node, self).setPos(currPos.x() + x, currPos.y() + y) # Prior to moving the node, we need to tell the connections to prepare for a geometry change. # This method must be called preior to moving a node. def prepareConnectionGeometryChange(self): for port in self.__ports: if port.inCircle(): for connection in port.inCircle().getConnections(): connection.prepareGeometryChange() if port.outCircle(): for connection in port.outCircle().getConnections(): connection.prepareGeometryChange() ######################### ## Ports def addPort(self, port): if isinstance(port, InputPort): self.__inputPortsHolder.addPort( port, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) elif isinstance(port, OutputPort): self.__outputPortsHolder.addPort( port, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) else: self.__ioPortsHolder.addPort( port, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) self.__ports.append(port) self.adjustSize() return port def getPort(self, name): for port in self.__ports: if port.getName() == name: return port return None def getInputPort(self, name): for port in self.__ports: if port.getName() == name and isinstance(port, (InputPort, IOPort)): return port return None def getOutputPort(self, name): for port in self.__ports: if port.getName() == name and isinstance(port, (OutputPort, IOPort)): return port return None def paint(self, painter, option, widget): rect = self.windowFrameRect() painter.setBrush(self.__color) painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0, 0), 0)) roundingY = 10 roundingX = rect.height() / rect.width() * roundingY painter.drawRoundRect(rect, roundingX, roundingY) # Title BG titleHeight = self.__headerItem.size().height() - 3 painter.setBrush(self.__color.darker(125)) roundingY = rect.width() * roundingX / titleHeight painter.drawRoundRect(0, 0, rect.width(), titleHeight, roundingX, roundingY) painter.drawRect(0, titleHeight * 0.5 + 2, rect.width(), titleHeight * 0.5) # painter.setPen(self.__linePen) # painter.drawLine(QtCore.QPoint(0, titleHeight), QtCore.QPoint(rect.width(), titleHeight)) painter.setBrush(QtGui.QColor(0, 0, 0, 0)) if self.__selected: painter.setPen(self.__selectedPen) else: painter.setPen(self.__unselectedPen) roundingY = 10 roundingX = rect.height() / rect.width() * roundingY painter.drawRoundRect(rect, roundingX, roundingY) ######################### ## Events def mousePressEvent(self, event): if event.button() is QtCore.Qt.MouseButton.LeftButton: modifiers = event.modifiers() if modifiers == QtCore.Qt.ControlModifier: if not self.isSelected(): self.__graph.selectNode(self, clearSelection=False) else: self.__graph.deselectNode(self) elif modifiers == QtCore.Qt.ShiftModifier: if not self.isSelected(): self.__graph.selectNode(self, clearSelection=False) else: if not self.isSelected(): self.__graph.selectNode(self, clearSelection=True) # Push all nodes back 1 level in z depth to bring selected # node to front for node in [x for x in self.__graph.getNodes().values()]: if node == self: continue if node.zValue() != 0.0: node.setZValue(node.zValue() - 1) self.__dragging = True self._mouseDownPoint = self.mapToScene(event.pos()) self._mouseDelta = self._mouseDownPoint - self.getGraphPos() self._lastDragPoint = self._mouseDownPoint self._nodesMoved = False else: super(Node, self).mousePressEvent(event) def mouseMoveEvent(self, event): if self.__dragging: newPos = self.mapToScene(event.pos()) graph = self.getGraph() if graph.getSnapToGrid() is True: gridSize = graph.getGridSize() newNodePos = newPos - self._mouseDelta snapPosX = math.floor(newNodePos.x() / gridSize) * gridSize snapPosY = math.floor(newNodePos.y() / gridSize) * gridSize snapPos = QtCore.QPointF(snapPosX, snapPosY) newPosOffset = snapPos - newNodePos newPos = newPos + newPosOffset delta = newPos - self._lastDragPoint self.__graph.moveSelectedNodes(delta) self._lastDragPoint = newPos self._nodesMoved = True else: super(Node, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self.__dragging: if self._nodesMoved: newPos = self.mapToScene(event.pos()) delta = newPos - self._mouseDownPoint self.__graph.endMoveSelectedNodes(delta) self.setCursor(QtCore.Qt.ArrowCursor) self.__dragging = False else: super(Node, self).mouseReleaseEvent(event) ######################### ## shut down def disconnectAllPorts(self): # gather all the connections into a list, and then remove them from the graph. # This is because we can't remove connections from ports while # iterating over the set. connections = [] for port in self.__ports: if port.inCircle(): for connection in port.inCircle().getConnections(): connections.append(connection) if port.outCircle(): for connection in port.outCircle().getConnections(): connections.append(connection) for connection in connections: self.__graph.removeConnection(connection)
class KGraphViewWidget(GraphViewWidget): rigNameChanged = QtCore.Signal () def __init__(self, parent=None): # constructors of base classes super(KGraphViewWidget, self).__init__(parent) self._builder = None self._guideBuilder = None self.guideRig = None graphView = KGraphView (parent = self) self.setGraphView (graphView) self.newRigPreset () # ============ # Rig Methods # ============ def editRigName(self): dialog = QtWidgets.QInputDialog(self) dialog.setObjectName('RigNameDialog') text, ok = dialog.getText(self, 'Edit Rig Name', 'New Rig Name', text=self.guideRig.getName()) if ok is True: self.setGuideRigName(text) def setGuideRigName(self, text): if text.endswith('_guide') is True: text = text.replace('_guide', '') self.guideRig.setName(text) self.rigNameChanged.emit() def newRigPreset(self): try: print "KGraphViewWidget.newRigPreset" self.guideRig = Rig() graphview=self.getGraphView() graphview.displayGraph(self.guideRig) self.setGuideRigName('MyRig') self.openedFile = None self.window().setWindowTitle('Beam Editor') print "newRigPreset,New Rig Created" except: print "newRigPreset,Error Creating New Rig" def buildGuideRig(self): self.window ().setCursor (QtCore.Qt.WaitCursor) if self.guideRig.getName().endswith('_guide') is False: self.guideRig.setName(self.guideRig.getName() + '_guide') if self._guideBuilder: self._guideBuilder.deleteBuildElements () self._guideBuilder = plugins.getBuilder () self._guideBuilder.build (self.guideRig) self.window ().setCursor (QtCore.Qt.ArrowCursor) def buildRig(self): print "buildRig" # ============== # Other Methods # ============== def addBackdrop(self, name='Backdrop'): graphView = self.getGraphView() initName = name suffix = 1 collision = True while collision: collision = graphView.hasNode(name) if not collision: break result = re.split(r"(\d+)$", initName, 1) if len(result) > 1: initName = result[0] suffix = int(result[1]) name = initName + str(suffix).zfill(2) suffix += 1 backdropNode = KBackdrop(graphView, name) graphView.addNode(backdropNode) graphView.selectNode(backdropNode, clearSelection=True) return backdropNode
def getGraphPos(self): transform = self.transform() size = self.size() return QtCore.QPointF(transform.dx() + (size.width() * 0.5), transform.dy() + (size.height() * 0.5))