Пример #1
0
    def dragObject(self):

        if not self.selectedIndexes():
            return

        item = self.selectedItems()[0]
        role = item.data(0, QtCore.Qt.UserRole)

        if role == 'Folder':
            return

        text = 'KrakenComponent:' + 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))

        drag.setPixmap(ghostComponent)
        drag.start(QtCore.Qt.IgnoreAction)
Пример #2
0
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)
Пример #3
0
 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())
Пример #4
0
    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)
Пример #5
0
    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)
Пример #6
0
    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
Пример #7
0
    def __init__(self, parent=None):
        super(KrakenWindow, self).__init__(parent)
        self.setObjectName('KrakenMainWindow')
        self.setWindowTitle('Kraken Editor')
        self.setWindowIcon(QtGui.QIcon(':/images/Kraken_Icon.png'))
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.installEventFilter(self)
        # self.setFocusPolicy(QtCore.Qt.StrongFocus)
        # self.setFocus()

        QtCore.QCoreApplication.setOrganizationName("Kraken")
        QtCore.QCoreApplication.setApplicationName("Kraken Editor")
        self.settings = QtCore.QSettings("Kraken", "Kraken Editor")
        self.preferences = Preferences()

        self._focusInCallbacks = []
        self._focusOutCallbacks = []

        cssPath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                               'kraken_ui.css')

        styleData = ''
        with open(cssPath) as cssFile:
            styleData = cssFile.read()

        self.setStyleSheet(styleData)

        self.createLayout()
        self.createConnections()
Пример #8
0
def generateNodes(count, offset, depth):
    for i in range(count):
        node1 = Node(graph, 'node' + str(depth) + str(i))
        node1.addPort(
            InputPort(node1, graph, 'InPort', QtGui.QColor(128, 170, 170, 255),
                      'MyDataX'))
        node1.addPort(
            OutputPort(node1, graph, 'OutPort', QtGui.QColor(32, 255, 32, 255),
                       'MyDataX'))
        node1.setGraphPos(QtCore.QPointF(offset, i * 80))

        graph.addNode(node1)

        global totalCount
        totalCount += 1

    if depth < 6:
        generateNodes(count * 2, offset + 160, depth + 1)

        for i in range(count):
            graph.connectPorts('node' + str(depth) + str(i), 'OutPort',
                               'node' + str(depth + 1) + str(i * 2), 'InPort')
            graph.connectPorts('node' + str(depth) + str(i), 'OutPort',
                               'node' + str(depth + 1) + str(i * 2 + 1),
                               'InPort')
    elif depth < 12:
        generateNodes(int(count / 2), offset + 160, depth + 1)

        for i in range(count / 2):
            graph.connectPorts('node' + str(depth) + str(i), 'OutPort',
                               'node' + str(depth + 1) + str(int(i)), 'InPort')
Пример #9
0
    def setData(self, data):
        """Sets the data on a backdrop after loading.

        Args:
            data (dict): Name, comment, graph pos, size, and color.

        Returns:
            bool: True if successful.

        """

        self.setComment(data.get('comment', ''))

        size = data.get('size', (self.minimumWidth(), self.minimumHeight()))
        self.resize(size[0], size[1])

        position = data.get('graphPos', (0, 0))
        self.setGraphPos(QtCore.QPointF(position[0], position[1]))

        color = data.get('color', self.__defaultColor.toTuple())
        self.setColor(
            color=QtGui.QColor(color[0], color[1], color[2], color[3]))
        self.setUnselectedColor(self.getColor().darker(125))
        self.setSelectedColor(self.getColor().lighter(175))
        self.setHoveredColor(self.getColor().lighter(110))

        return True
Пример #10
0
    def pasteMirroredConnected(self):
        graphView = self.getGraphView()
        clipboardData = self.graphView.getClipboardData()

        pos = clipboardData['copyPos'] + QtCore.QPoint(20, 20)
        graphView.pasteSettings(pos,
                                mirrored=True,
                                createConnectionsToExistingNodes=True)
Пример #11
0
    def getEdge(self, pos):
        topRectUpperLeft = self.mapFromItem(self,
                                            self.boundingRect().topLeft())
        topRectLowerRight = self.mapFromItem(self,
                                             self.boundingRect().right(),
                                             self.__resizeDistance * 0.25)
        topRect = QtCore.QRectF(topRectUpperLeft, topRectLowerRight)

        rightRectUpperLeft = self.mapFromItem(
            self,
            self.boundingRect().right() - self.__resizeDistance * 0.25, 0)
        rightRectLowerRight = self.mapFromItem(
            self,
            self.boundingRect().bottomRight())
        rightRect = QtCore.QRectF(rightRectUpperLeft, rightRectLowerRight)

        bottomRectUpperLeft = self.mapFromItem(
            self, 0,
            self.boundingRect().bottom() - self.__resizeDistance * 0.25)
        bottomRectLowerRight = self.mapFromItem(
            self,
            self.boundingRect().bottomRight())
        bottomRect = QtCore.QRectF(bottomRectUpperLeft, bottomRectLowerRight)

        leftRectUpperLeft = self.mapFromItem(self,
                                             self.boundingRect().topLeft())
        leftRectLowerRight = self.mapFromItem(
            self,
            self.boundingRect().left() + self.__resizeDistance * 0.25,
            self.boundingRect().bottom())
        leftRect = QtCore.QRectF(leftRectUpperLeft, leftRectLowerRight)

        if topRect.contains(pos):
            return -1  # Disabling the top resize by default.
        elif rightRect.contains(pos):
            return 1
        elif bottomRect.contains(pos):
            return 2
        elif leftRect.contains(pos):
            return 3

        return -1
Пример #12
0
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()
Пример #13
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)
Пример #14
0
    def getCorner(self, pos):
        topLeft = self.mapFromItem(self, self.boundingRect().topLeft())
        bottomRight = self.mapFromItem(self, self.boundingRect().bottomRight())
        rect = QtCore.QRectF(topLeft, bottomRight)

        if (rect.topLeft() - pos).manhattanLength() < self.__resizeDistance:
            return 0
        elif (rect.topRight() - pos).manhattanLength() < self.__resizeDistance:
            return 1
        elif (rect.bottomLeft() -
              pos).manhattanLength() < self.__resizeDistance:
            return 2
        elif (rect.bottomRight() -
              pos).manhattanLength() < self.__resizeDistance:
            return 3

        return -1
Пример #15
0
    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()
Пример #16
0
    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()
Пример #17
0
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
Пример #18
0
    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)
Пример #19
0
    def __init__(self, graph, component):
        super(KNode, self).__init__(graph, component.getDecoratedName())

        self.__component = component
        self.__inspectorWidget = None

        for i in range(self.__component.getNumInputs()):
            componentInput = component.getInputByIndex(i)
            self.addPort(KNodeInputPort(self, graph, componentInput))

        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))

        nodeColor = component.getComponentColor()
        self.setColor(
            QtGui.QColor(nodeColor[0], nodeColor[1], nodeColor[2],
                         nodeColor[3]))
        self.setUnselectedColor(self.getColor().darker(125))
        self.setSelectedColor(self.getColor().lighter(175))
Пример #20
0
    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)
Пример #21
0
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
Пример #22
0
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)
Пример #23
0
 def getGraphPos(self):
     transform = self.transform()
     size = self.size()
     return QtCore.QPointF(transform.dx()+(size.width()*0.5), transform.dy()+(size.height()*0.5))
Пример #24
0
 def minimumSizeHint(self):
     return QtCore.QSize(10, 25)
Пример #25
0
    node1.addPort(
        InputPort(node1, graph, 'InPort1', QtGui.QColor(128, 170, 170, 255),
                  'MyDataX'))
    node1.addPort(
        InputPort(node1, graph, 'InPort2', QtGui.QColor(128, 170, 170, 255),
                  'MyDataX'))
    node1.addPort(
        OutputPort(node1, graph, 'OutPort', QtGui.QColor(32, 255, 32, 255),
                   'MyDataY'))
    node1.addPort(
        IOPort(node1, graph, 'IOPort1', QtGui.QColor(32, 255, 32, 255),
               'MyDataY'))
    node1.addPort(
        IOPort(node1, graph, 'IOPort2', QtGui.QColor(32, 255, 32, 255),
               'MyDataY'))
    node1.setGraphPos(QtCore.QPointF(-100, 0))

    graph.addNode(node1)

    node2 = Node(graph, 'ReallyLongLabel')
    node2.addPort(
        InputPort(node2, graph, 'InPort1', QtGui.QColor(128, 170, 170, 255),
                  'MyDataX'))
    node2.addPort(
        InputPort(node2, graph, 'InPort2', QtGui.QColor(128, 170, 170, 255),
                  'MyDataX'))
    node2.addPort(
        OutputPort(node2, graph, 'OutPort', QtGui.QColor(32, 255, 32, 255),
                   'MyDataY'))
    node2.addPort(
        IOPort(node2, graph, 'IOPort1', QtGui.QColor(32, 255, 32, 255),
Пример #26
0
 def textSize(self):
     return QtCore.QSizeF(
         self.__textItem.textWidth(),
         self.__font.pointSizeF() + self.__labelBottomSpacing
         )
Пример #27
0
    def write(self, msg, level):

        messageConfig = {
            'DEBUG': {
                'color': '#000000',
                'background-color': '#EEE97B',
                'timeout': 3500
            },
            'INFO': {
                'color': '#000000',
                'background-color': '#FFFFFF',
                'timeout': 3500
            },
            'INFORM': {
                'color': '#FFFFFF',
                'background-color': '#009900',
                'timeout': 3500
            },
            'WARNING': {
                'color': '#FFFFFF',
                'background-color': '#D89614',
                'timeout': 3500
            },
            'ERROR': {
                'color': '#FFFFFF',
                'background-color': '#CC0000',
                'timeout': 0
            },
            'CRITICAL': {
                'color': '#FFFFFF',
                'background-color': '#CC0000',
                'timeout': 0
            }
        }

        timeOut = messageConfig[level]['timeout']

        # Remove current labels
        currentLabels = self.findChildren(QtWidgets.QLabel)
        for label in currentLabels:
            self.removeWidget(label)

        if level == "INFO":
            self.showMessage(msg, timeOut)
        else:
            lines = msg.split("\n")
            if len(lines) > 0:
                msg = lines[0][:120]
            else:
                msg = msg[:120]

            messageLabel = QtWidgets.QLabel(' ' + msg)
            messageLabel.setStyleSheet("QLabel { border-radius: 3px; background-color: " + messageConfig[level]['background-color'] + "; color: " + messageConfig[level]['color'] + "}")

            def addMessage():
                self.clearMessage()
                self.currentMessage = messageLabel
                self.addWidget(messageLabel, 1)
                self.repaint()
                if timeOut > 0.0:
                    timer.start()

            def endMessage():
                timer.stop()
                self.removeWidget(messageLabel)
                self.repaint()
                self.showMessage('Ready', 2000)

            if timeOut > 0.0:
                timer = QtCore.QTimer()
                timer.setInterval(timeOut)
                timer.timeout.connect(endMessage)

            addMessage()
Пример #28
0
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)

    ################################################
    ## Connections

    def emitBeginConnectionManipulationSignal(self):
        self.beginConnectionManipulation.emit()

    def emitEndConnectionManipulationSignal(self):
        self.endConnectionManipulation.emit()

    def addConnection(self, connection, emitSignal=True):

        self.__connections.add(connection)
        self.scene().addItem(connection)
        if emitSignal:
            self.connectionAdded.emit(connection)
        return connection

    def removeConnection(self, connection, emitSignal=True):

        connection.disconnect()
        self.__connections.remove(connection)
        self.scene().removeItem(connection)
        if emitSignal:
            self.connectionRemoved.emit(connection)

    def connectPorts(self, srcNode, outputName, tgtNode, inputName):

        if isinstance(srcNode, Node):
            sourceNode = srcNode
        elif isinstance(srcNode, basestring):
            sourceNode = self.getNode(srcNode)
            if not sourceNode:
                raise Exception("Node not found:" + str(srcNode))
        else:
            raise Exception("Invalid srcNode:" + str(srcNode))

        sourcePort = sourceNode.getOutputPort(outputName)
        if not sourcePort:
            raise Exception("Node '" + sourceNode.getName() +
                            "' does not have output:" + outputName)

        if isinstance(tgtNode, Node):
            targetNode = tgtNode
        elif isinstance(tgtNode, basestring):
            targetNode = self.getNode(tgtNode)
            if not targetNode:
                raise Exception("Node not found:" + str(tgtNode))
        else:
            raise Exception("Invalid tgtNode:" + str(tgtNode))

        targetPort = targetNode.getInputPort(inputName)
        if not targetPort:
            raise Exception("Node '" + targetNode.getName() +
                            "' does not have input:" + inputName)

        connection = Connection(self, sourcePort.outCircle(),
                                targetPort.inCircle())
        self.addConnection(connection, emitSignal=False)

        return connection

    ################################################
    ## 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()

    ################################################
    ## Painting

    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)
Пример #29
0
 def textSize(self):
     return QtCore.QSizeF(self.__fontMetrics.width(self.__text),
                          self.__fontMetrics.height())
Пример #30
0
class KGraphViewWidget(GraphViewWidget):

    rigNameChanged = QtCore.Signal()
    rigLoaded = QtCore.Signal(object)
    rigLoadedConfig = QtCore.Signal(object)

    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)
        graphView.nodeAdded.connect(self.__onNodeAdded)
        graphView.nodeRemoved.connect(self.__onNodeRemoved)
        graphView.beginConnectionManipulation.connect(
            self.__onBeginConnectionManipulation)
        graphView.endConnectionManipulation.connect(
            self.__onEndConnectionManipulationSignal)
        graphView.connectionAdded.connect(self.__onConnectionAdded)
        graphView.connectionRemoved.connect(self.__onConnectionRemoved)

        graphView.selectionChanged.connect(self.__onSelectionChanged)
        graphView.endSelectionMoved.connect(self.__onSelectionMoved)

        graphView.beginDeleteSelection.connect(self.__onBeginDeleteSelection)
        graphView.endDeleteSelection.connect(self.__onEndDeleteSelection)

        self.setGraphView(graphView)

        # =========================================
        # Setup hotkeys for the following actions.
        # =========================================

        undoShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Z), self)
        undoShortcut.activated.connect(self.undo)

        redoShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_Y), self)
        redoShortcut.activated.connect(self.redo)

        openContextualNodeListShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence(QtCore.Qt.Key_Tab), self)
        openContextualNodeListShortcut.activated.connect(
            self.openContextualNodeList)

        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:
            self.guideRig = Rig()
            self.getGraphView().displayGraph(self.guideRig)
            self.setGuideRigName('MyRig')

            self.openedFile = None

            self.window().setWindowTitle('Kraken Editor')

            logger.inform("New Rig Created")
        except:
            logger.exception("Error Creating New Rig")

    def saveRig(self, saveAs=False):
        """Saves the current rig to disc.

        Args:
            saveAs (Boolean): Determines if this was a save as call or just a normal save.

        Returns:
            String: Path to the saved file.

        """

        try:
            self.window().setCursor(QtCore.Qt.WaitCursor)

            filePath = self.openedFile

            if saveAs is True or not filePath or not os.path.isdir(
                    os.path.dirname(filePath)):

                settings = self.window().getSettings()
                settings.beginGroup('Files')
                lastFilePath = settings.value(
                    "lastFilePath",
                    os.path.join(GetKrakenPath(), self.guideRig.getName()))
                settings.endGroup()

                filePathDir = os.path.dirname(lastFilePath)

                if not os.path.isdir(filePathDir):
                    filePathDir = GetKrakenPath()

                fileDialog = QtWidgets.QFileDialog(self)
                fileDialog.setOption(QtWidgets.QFileDialog.DontUseNativeDialog,
                                     on=True)
                fileDialog.setWindowTitle('Save Rig Preset As')
                fileDialog.setDirectory(os.path.abspath(filePathDir))
                fileDialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
                fileDialog.setNameFilter('Kraken Rig (*.krg)')
                fileDialog.setDefaultSuffix('krg')

                if fileDialog.exec_() == QtWidgets.QFileDialog.Accepted:
                    filePath = fileDialog.selectedFiles()[0]
                else:
                    return False

            self.synchGuideRig()

            # Backdrop Meta Data
            graphView = self.getGraphView()
            backdropNodes = graphView.getNodesOfType('KBackdrop')
            backdropData = [x.getData() for x in backdropNodes]

            # =====================
            # Add Meta Data to rig
            # =====================
            self.guideRig.setMetaDataItem('backdrops', backdropData)

            currConfig = Config.getInstance()
            if currConfig.getModulePath(
            ) == "kraken.core.configs.config.Config":
                self.guideRig.setMetaDataItem('config', "Default Config")
            else:
                self.guideRig.setMetaDataItem('config',
                                              currConfig.getModulePath())

            # Write rig file
            try:
                self.guideRig.writeRigDefinitionFile(filePath)
                settings = self.window().getSettings()
                settings.beginGroup('Files')
                settings.setValue("lastFilePath", filePath)
                settings.endGroup()
                self.openedFile = filePath

                logger.inform('Saved Rig file: ' + filePath)

            except:
                logger.exception('Error Saving Rig File')
                return False

            return filePath

        finally:
            self.window().setCursor(QtCore.Qt.ArrowCursor)

    def saveAsRigPreset(self):
        """Opens a dialogue window to save the current rig as a different file."""

        filePath = self.saveRig(saveAs=True)
        if filePath is not False:
            self.window().setWindowTitle('Kraken Editor - ' + filePath + '[*]')

        self.rigLoaded.emit(self.openedFile)

    def saveRigPreset(self):

        if self.openedFile is None or not os.path.exists(self.openedFile):
            self.saveAsRigPreset()

        else:
            self.saveRig(saveAs=False)
            self.rigLoaded.emit(self.openedFile)

    def openRigPreset(self):

        try:
            self.window().setCursor(QtCore.Qt.WaitCursor)

            settings = self.window().getSettings()
            settings.beginGroup('Files')
            lastFilePath = settings.value(
                "lastFilePath",
                os.path.join(GetKrakenPath(), self.guideRig.getName()))
            settings.endGroup()

            if not lastFilePath:
                lastFilePath = GetKrakenPath()

            fileDialog = QtWidgets.QFileDialog(self)
            fileDialog.setOption(QtWidgets.QFileDialog.DontUseNativeDialog,
                                 on=True)
            fileDialog.setWindowTitle('Open Rig Preset')
            fileDialog.setDirectory(
                os.path.dirname(os.path.abspath(lastFilePath)))
            fileDialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
            fileDialog.setNameFilter('Kraken Rig (*.krg)')

            if fileDialog.exec_() == QtWidgets.QFileDialog.Accepted:
                filePath = fileDialog.selectedFiles()[0]
                self.loadRigPreset(filePath)

        finally:
            self.window().setCursor(QtCore.Qt.ArrowCursor)

    def loadRigPreset(self, filePath):

        if not os.path.exists(filePath):
            logger.warn("File '" + filePath + "' does not exist!")
            return

        self.guideRig = Rig()
        self.guideRig.loadRigDefinitionFile(filePath)

        self.setGuideRigName(self.guideRig.getName())

        rigConfig = self.guideRig.getMetaDataItem('config')
        if rigConfig is not None:
            self.rigLoadedConfig.emit(rigConfig)

        self.graphView.displayGraph(self.guideRig)

        settings = self.window().getSettings()
        settings.beginGroup('Files')
        settings.setValue("lastFilePath", filePath)
        settings.endGroup()

        self.openedFile = filePath
        self.window().setWindowTitle('Kraken Editor - ' + filePath + '[*]')
        logger.inform('Loaded Rig file: ' + filePath)

        self.rigLoaded.emit(filePath)

    def buildGuideRig(self):

        try:
            logger.info('Building Guide')
            self.window().setCursor(QtCore.Qt.WaitCursor)

            initConfigIndex = self.window(
            ).krakenMenu.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)

            logger.inform('Guide Rig Build Success')

            self.window().krakenMenu.setCurrentConfig(initConfigIndex)

        except:
            logger.exception('Error Building')

        finally:
            self.window().setCursor(QtCore.Qt.ArrowCursor)

    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()

    def buildRig(self):

        try:
            self.window().setCursor(QtCore.Qt.WaitCursor)

            self.window().statusBar.showMessage('Building Rig')

            initConfigIndex = self.window(
            ).krakenMenu.configsWidget.currentIndex()

            self.synchGuideRig()

            rigBuildData = self.guideRig.getRigBuildData()
            rig = Rig()
            rig.loadRigDefinition(rigBuildData)

            rig.setName(rig.getName().replace('_guide', ''))

            if self.window().preferences.getPreferenceValue(
                    'delete_existing_rigs'):
                if self._builder:
                    self._builder.deleteBuildElements()

            self._builder = plugins.getBuilder()
            self._builder.build(rig)

            logger.inform('Rig Build Success')

            self.window().krakenMenu.setCurrentConfig(initConfigIndex)

        except Exception as e:
            logger.exception('Error Building')

        finally:
            self.window().setCursor(QtCore.Qt.ArrowCursor)

    # ==========
    # Shortcuts
    # ==========
    def copy(self):
        graphView = self.getGraphView()
        pos = graphView.getSelectedNodesCentroid()
        graphView.copySettings(pos)

    def paste(self):
        graphView = self.getGraphView()
        clipboardData = self.graphView.getClipboardData()

        pos = clipboardData['copyPos'] + QtCore.QPoint(20, 20)
        graphView.pasteSettings(pos,
                                mirrored=False,
                                createConnectionsToExistingNodes=True)

    def pasteUnconnected(self):
        graphView = self.getGraphView()
        clipboardData = self.graphView.getClipboardData()

        pos = clipboardData['copyPos'] + QtCore.QPoint(20, 20)
        graphView.pasteSettings(pos,
                                mirrored=False,
                                createConnectionsToExistingNodes=False)

    def pasteMirrored(self):
        graphView = self.getGraphView()
        clipboardData = self.graphView.getClipboardData()

        pos = clipboardData['copyPos'] + QtCore.QPoint(20, 20)
        graphView.pasteSettings(pos,
                                mirrored=True,
                                createConnectionsToExistingNodes=False)

    def pasteMirroredConnected(self):
        graphView = self.getGraphView()
        clipboardData = self.graphView.getClipboardData()

        pos = clipboardData['copyPos'] + QtCore.QPoint(20, 20)
        graphView.pasteSettings(pos,
                                mirrored=True,
                                createConnectionsToExistingNodes=True)

    def undo(self):
        UndoRedoManager.getInstance().undo()

    def redo(self):
        UndoRedoManager.getInstance().redo()

    def openContextualNodeList(self):
        pos = self.mapFromGlobal(QtGui.QCursor.pos())

        contextualNodeList = ContextualNodeList(self)

        scenepos = self.graphView.mapToScene(pos)
        contextualNodeList.showAtPos(pos, scenepos, self.graphView)

    # ==============
    # Other Methods
    # ==============
    def addBackdrop(self, name='Backdrop'):
        """Adds a backdrop node to the graph.

        Args:
            name (str): Name of the backdrop node.

        Returns:
            Node: Backdrop node that was created.

        """

        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

    # ===============
    # Signal Handlers
    # ===============
    def __onNodeAdded(self, node):
        if not UndoRedoManager.getInstance().isUndoingOrRedoing():
            command = graph_commands.AddNodeCommand(self.graphView,
                                                    self.guideRig, node)
            UndoRedoManager.getInstance().addCommand(command)

    def __onNodeRemoved(self, node):

        if type(node).__name__ != 'KBackdrop':
            node.getComponent().detach()

        if not UndoRedoManager.getInstance().isUndoingOrRedoing():
            command = graph_commands.RemoveNodeCommand(self.graphView,
                                                       self.guideRig, node)
            UndoRedoManager.getInstance().addCommand(command)

    def __onBeginConnectionManipulation(self):
        UndoRedoManager.getInstance().openBracket('Connect Ports')

    def __onEndConnectionManipulationSignal(self):
        UndoRedoManager.getInstance().closeBracket()

    def __onConnectionAdded(self, connection):
        if not UndoRedoManager.getInstance().isUndoingOrRedoing():
            command = graph_commands.ConnectionAddedCommand(
                self.graphView, self.guideRig, connection)
            UndoRedoManager.getInstance().addCommand(command)

    def __onConnectionRemoved(self, connection):
        if not UndoRedoManager.getInstance().isUndoingOrRedoing():
            command = graph_commands.ConnectionRemovedCommand(
                self.graphView, self.guideRig, connection)
            UndoRedoManager.getInstance().addCommand(command)

    def __onSelectionChanged(self, deselectedNodes, selectedNodes):
        if not UndoRedoManager.getInstance().isUndoingOrRedoing():
            command = graph_commands.SelectionChangeCommand(
                self.graphView, deselectedNodes, selectedNodes)
            UndoRedoManager.getInstance().addCommand(command)

    def __onSelectionMoved(self, nodes, delta):
        if not UndoRedoManager.getInstance().isUndoingOrRedoing():
            command = graph_commands.NodesMoveCommand(self.graphView, nodes,
                                                      delta)
            UndoRedoManager.getInstance().addCommand(command)

    def __onBeginDeleteSelection(self):
        UndoRedoManager.getInstance().openBracket('Delete Nodes')

    def __onEndDeleteSelection(self):
        UndoRedoManager.getInstance().closeBracket()