示例#1
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)
示例#2
0
    def __init__(self, port, graph, hOffset, color, connectionPointType):
        super(KNodePortCircle, self).__init__(port, graph, hOffset, color,
                                              connectionPointType)

        if self.getPort().getDataType().endswith('[]'):
            self.setDefaultPen(QtGui.QPen(QtGui.QColor(204, 0, 0), 1.5))
            self.setHoverPen(QtGui.QPen(QtGui.QColor(255, 155, 100), 2.0))
示例#3
0
    def __init__(self, graph, srcPortCircle, dstPortCircle):
        super(Connection, self).__init__()

        self.__graph = graph
        self.__srcPortCircle = srcPortCircle
        self.__dstPortCircle = dstPortCircle
        penStyle = QtCore.Qt.DashLine

        self.__connectionColor = QtGui.QColor(0, 0, 0)
        self.__connectionColor.setRgbF(*self.__srcPortCircle.getColor().getRgbF())
        self.__connectionColor.setAlpha(125)

        self.__defaultPen = QtGui.QPen(self.__connectionColor, 1.5, s=penStyle)
        self.__defaultPen.setDashPattern([1, 2, 2, 1])

        self.__connectionHoverColor = QtGui.QColor(0, 0, 0)
        self.__connectionHoverColor.setRgbF(*self.__srcPortCircle.getColor().getRgbF())
        self.__connectionHoverColor.setAlpha(255)

        self.__hoverPen = QtGui.QPen(self.__connectionHoverColor, 1.5, s=penStyle)
        self.__hoverPen.setDashPattern([1, 2, 2, 1])

        self.setPen(self.__defaultPen)
        self.setZValue(-1)

        self.setAcceptHoverEvents(True)
        self.connect()
示例#4
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):
        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
示例#5
0
    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)
示例#6
0
class SelectionRect(QtWidgets.QGraphicsWidget):
    __backgroundColor = QtGui.QColor(100, 100, 100, 50)
    __pen = QtGui.QPen(QtGui.QColor(25, 25, 25), 1.0, QtCore.Qt.DashLine)

    def __init__(self, graph, mouseDownPos):
        super(SelectionRect, self).__init__()
        self.setZValue(-1)

        self.__graph = graph
        self.__graph.scene().addItem(self)
        self.__mouseDownPos = mouseDownPos
        self.setPos(self.__mouseDownPos)
        self.resize(0, 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())

    def paint(self, painter, option, widget):
        rect = self.windowFrameRect()
        painter.setBrush(self.__backgroundColor)
        painter.setPen(self.__pen)
        painter.drawRect(rect)

    def destroy(self):
        self.__graph.scene().removeItem(self)
示例#7
0
    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
示例#8
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)

    ################################################
    ## 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
示例#9
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)
示例#10
0
class Connection(QtWidgets.QGraphicsPathItem):
    __defaultPen = QtGui.QPen(QtGui.QColor(168, 134, 3), 1.5)

    def __init__(self, graph, srcPortCircle, dstPortCircle):
        super(Connection, self).__init__()

        self.__graph = graph
        self.__srcPortCircle = srcPortCircle
        self.__dstPortCircle = dstPortCircle
        penStyle = QtCore.Qt.DashLine

        self.__connectionColor = QtGui.QColor(0, 0, 0)
        self.__connectionColor.setRgbF(*self.__srcPortCircle.getColor().getRgbF())
        self.__connectionColor.setAlpha(125)

        self.__defaultPen = QtGui.QPen(self.__connectionColor, 1.5, s=penStyle)
        self.__defaultPen.setDashPattern([1, 2, 2, 1])

        self.__connectionHoverColor = QtGui.QColor(0, 0, 0)
        self.__connectionHoverColor.setRgbF(*self.__srcPortCircle.getColor().getRgbF())
        self.__connectionHoverColor.setAlpha(255)

        self.__hoverPen = QtGui.QPen(self.__connectionHoverColor, 1.5, s=penStyle)
        self.__hoverPen.setDashPattern([1, 2, 2, 1])

        self.setPen(self.__defaultPen)
        self.setZValue(-1)

        self.setAcceptHoverEvents(True)
        self.connect()


    def setPenStyle(self, penStyle):
        self.__defaultPen.setStyle(penStyle)
        self.__hoverPen.setStyle(penStyle)
        self.setPen(self.__defaultPen) # Force a redraw


    def setPenWidth(self, width):
        self.__defaultPen.setWidthF(width)
        self.__hoverPen.setWidthF(width)
        self.setPen(self.__defaultPen) # Force a redraw


    def getSrcPortCircle(self):
        return self.__srcPortCircle


    def getDstPortCircle(self):
        return self.__dstPortCircle


    def getSrcPort(self):
        return self.__srcPortCircle.getPort()


    def getDstPort(self):
        return self.__dstPortCircle.getPort()


    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)


    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 hoverEnterEvent(self, event):
        self.setPen(self.__hoverPen)
        super(Connection, self).hoverEnterEvent(event)


    def hoverLeaveEvent(self, event):
        self.setPen(self.__defaultPen)
        super(Connection, self).hoverLeaveEvent(event)


    def mousePressEvent(self, event):
        if event.button() is QtCore.Qt.MouseButton.LeftButton:
            self.__dragging = True
            self._lastDragPoint = self.mapToScene(event.pos())
            event.accept()
        else:
            super(Connection, self).mousePressEvent(event)


    def mouseMoveEvent(self, event):
        if self.__dragging:
            pos = self.mapToScene(event.pos())
            delta = pos - self._lastDragPoint
            if delta.x() != 0:

                self.__graph.removeConnection(self)

                import mouse_grabber
                if delta.x() < 0:
                    mouse_grabber.MouseGrabber(self.__graph, pos, self.__srcPortCircle, 'In')
                else:
                    mouse_grabber.MouseGrabber(self.__graph, pos, self.__dstPortCircle, 'Out')

        else:
            super(Connection, self).mouseMoveEvent(event)


    def disconnect(self):
        self.__srcPortCircle.removeConnection(self)
        self.__dstPortCircle.removeConnection(self)


    def connect(self):
        self.__srcPortCircle.addConnection(self)
        self.__dstPortCircle.addConnection(self)