Example #1
0
    def __init__(self, highlight_level, bounding_box, label, shape, color=None, parent=None, label_pos=None, tooltip=None):
        super(NodeItem, self).__init__(highlight_level, parent)

        self._default_color = self._COLOR_BLACK if color is None else color
        self._brush = QBrush(self._default_color)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._ellipse_pen = QPen(self._label_pen)
        self._ellipse_pen.setWidth(1)

        self._incoming_edges = set()
        self._outgoing_edges = set()

        self.parse_shape(shape, bounding_box)
        self.addToGroup(self._graphics_item)

        self._label = QGraphicsSimpleTextItem(label)
        self._label.setFont(GraphItem._LABEL_FONT)
        label_rect = self._label.boundingRect()
        if label_pos is None:
            label_rect.moveCenter(bounding_box.center())
        else:
            label_rect.moveCenter(label_pos)
        self._label.setPos(label_rect.x(), label_rect.y())
        self.addToGroup(self._label)
        if tooltip is not None:
            self.setToolTip(tooltip)

        self.set_node_color()

        self.setAcceptHoverEvents(True)

        self.hovershape = None
Example #2
0
    def __init__(self,
                 bounding_box,
                 shape,
                 label,
                 label_pos=None,
                 url=None,
                 parent=None,
                 **kwargs):
        super(NodeItem, self).__init__(parent, **kwargs)

        self.url = url
        self._incoming_edges = set()
        self._outgoing_edges = set()
        self._brush = QBrush(self._color)

        self._label_pen = QPen()
        self._label_pen.setColor(self._color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._label_pen.setWidthF(self._label_pen_width)

        if self._pen_width == 0.0:
            self._graphics_item_pen = QPen(Qt.NoPen)
        else:
            self._graphics_item_pen = QPen(self._label_pen)
            self._graphics_item_pen.setWidthF(self._pen_width)

        self._graphics_item = ShapeFactory.create(shape, bounding_box)
        if ShapeFactory.message is not None:
            print ShapeFactory.message

        self.addToGroup(self._graphics_item)

        if not shape == 'point':
            self._label = QGraphicsSimpleTextItem(label)

            label_rectangle = self._label.boundingRect()
            if label_pos is None:
                label_rectangle.moveCenter(bounding_box.center())
            else:
                label_rectangle.moveCenter(label_pos)
            self._label.setPos(label_rectangle.x(), label_rectangle.y())

            self.addToGroup(self._label)
        else:
            self._graphics_item.setBrush(self._color)
            self._label = None

        self._brush.setColor(self._color)
        self._graphics_item_pen.setColor(self._color)
        self._label_pen.setColor(self._color)

        self._graphics_item.setPen(self._graphics_item_pen)

        if self._label is not None:
            self._label.setBrush(self._brush)
            self._label.setPen(self._label_pen)
    def __init__(self, track, text, starttime, duration):
        self._track = track
        super(TimelineClip, self).__init__(0.0, 0.0, duration, track.height, None, track.scene())
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setZValue(self._z)
        self.setBrush(QBrush(self._track._colors['item']))
        self.setPen(QPen(self._track._colors['item']))

        self._text = QGraphicsSimpleTextItem(text, self)
        self._text.setBrush(QColor(Qt.black))
        self._text.setFont(QFont('sans', 12))
        self._text.setPos(0.05, 0.05)
        self._text.setFlag(QGraphicsItem.ItemIgnoresTransformations)

        self.set_starttime(starttime)
        self.set_data([])
Example #4
0
    def __init__(self, bounding_box, shape, label, label_pos=None, url=None, parent=None, **kwargs):
        super(NodeItem, self).__init__(parent, **kwargs)

        self.url = url
        self._incoming_edges = set()
        self._outgoing_edges = set()
        self._brush = QBrush(self._color)

        self._label_pen = QPen()
        self._label_pen.setColor(self._color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._label_pen.setWidthF(self._label_pen_width)

        if self._pen_width == 0.0:
            self._graphics_item_pen = QPen(Qt.NoPen)
        else:
            self._graphics_item_pen = QPen(self._label_pen)
            self._graphics_item_pen.setWidthF(self._pen_width)

        self._graphics_item = ShapeFactory.create(shape, bounding_box)
        if ShapeFactory.message is not None:
            print ShapeFactory.message

        self.addToGroup(self._graphics_item)

        if not shape == 'point':
            self._label = QGraphicsSimpleTextItem(label)

            label_rectangle = self._label.boundingRect()
            if label_pos is None:
                label_rectangle.moveCenter(bounding_box.center())
            else:
                label_rectangle.moveCenter(label_pos)
            self._label.setPos(label_rectangle.x(), label_rectangle.y())

            self.addToGroup(self._label)
        else:
            self._graphics_item.setBrush(self._color)
            self._label = None

        self._brush.setColor(self._color)
        self._graphics_item_pen.setColor(self._color)
        self._label_pen.setColor(self._color)

        self._graphics_item.setPen(self._graphics_item_pen)

        if self._label is not None:
            self._label.setBrush(self._brush)
            self._label.setPen(self._label_pen)
Example #5
0
    def __init__(self, highlight_level, bounding_box, label, shape, color=None, parent=None, label_pos=None, tooltip=None):
        super(NodeItem, self).__init__(highlight_level, parent)

        self._default_color = self._COLOR_BLACK if color is None else color
        self._brush = QBrush(self._default_color)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._ellipse_pen = QPen(self._label_pen)
        self._ellipse_pen.setWidth(1)

        self._incoming_edges = set()
        self._outgoing_edges = set()

        if shape == 'box':
            self._graphics_item = QGraphicsRectItem(bounding_box)
        else:
            self._graphics_item = QGraphicsEllipseItem(bounding_box)
        self.addToGroup(self._graphics_item)

        self._label = QGraphicsSimpleTextItem(label)
        label_rect = self._label.boundingRect()
        if label_pos is None:
            label_rect.moveCenter(bounding_box.center())
        else:
            label_rect.moveCenter(label_pos)
        self._label.setPos(label_rect.x(), label_rect.y())
        self.addToGroup(self._label)
        if tooltip is not None:
            self.setToolTip(tooltip)

        self.set_node_color()

        self.setAcceptHoverEvents(True)

        self.hovershape = None
Example #6
0
    def __init__(self,
                 spline,
                 label,
                 label_center,
                 from_node,
                 to_node,
                 parent=None,
                 **kwargs):
        super(EdgeItem, self).__init__(parent, **kwargs)

        self._edge_pen_width = kwargs.get('edge_pen_width',
                                          self.EDGE_PEN_WIDTH)

        self.from_node = from_node
        self.from_node.add_outgoing_edge(self)

        self.to_node = to_node
        self.to_node.add_incoming_edge(self)

        self._brush = QBrush(self._color)

        self._label_pen = QPen()
        self._label_pen.setColor(self._color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._label_pen.setWidthF(self._label_pen_width)

        self._edge_pen = QPen()
        self._edge_pen.setColor(self._color)
        self._edge_pen.setWidthF(self._edge_pen_width)

        self._sibling_edges = set()

        self._label = None
        if label is not None:
            self._label = QGraphicsSimpleTextItem(label)

            font = self._label.font()
            font.setPointSize(8)
            self._label.setFont(font)

            label_rect = self._label.boundingRect()
            label_rect.moveCenter(label_center)
            self._label.setPos(label_rect.x(), label_rect.y())

        # spline specification according to http://www.graphviz.org/doc/info/attrs.html#k:splineType
        coordinates = spline.split(' ')
        # extract optional end_point
        end_point = None
        if coordinates[0].startswith('e,'):
            parts = coordinates.pop(0)[2:].split(',')
            end_point = QPointF(float(parts[0]), -float(parts[1]))
        # extract optional start_point
        if coordinates[0].startswith('s,'):
            parts = coordinates.pop(0).split(',')

        # first point
        parts = coordinates.pop(0).split(',')
        point = QPointF(float(parts[0]), -float(parts[1]))
        path = QPainterPath(point)

        while len(coordinates) > 2:
            # extract triple of points for a cubic spline
            parts = coordinates.pop(0).split(',')
            point1 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point2 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point3 = QPointF(float(parts[0]), -float(parts[1]))
            path.cubicTo(point1, point2, point3)

        self._arrow = None
        if end_point is not None:
            # draw arrow
            self._arrow = QGraphicsPolygonItem()
            polygon = QPolygonF()
            polygon.append(point3)
            offset = QPointF(end_point - point3)
            corner1 = QPointF(-offset.y(), offset.x()) * 0.35
            corner2 = QPointF(offset.y(), -offset.x()) * 0.35
            polygon.append(point3 + corner1)
            polygon.append(end_point)
            polygon.append(point3 + corner2)
            self._arrow.setPolygon(polygon)

        self._path = QGraphicsPathItem()
        self._path.setPath(path)
        self.addToGroup(self._path)

        self._brush.setColor(self._color)
        self._edge_pen.setColor(self._color)
        self._label_pen.setColor(self._color)

        self._path.setPen(self._edge_pen)
        if self._arrow is not None:
            self._arrow.setBrush(self._brush)
            self._arrow.setPen(self._edge_pen)
        if self._label is not None:
            self._label.setBrush(self._brush)
            self._label.setPen(self._label_pen)
Example #7
0
    def __init__(self, highlight_level, spline, label_center, label, from_node, to_node, parent=None, penwidth=1, edge_color=None, style='solid'):
        super(EdgeItem, self).__init__(highlight_level, parent)

        self.from_node = from_node
        self.from_node.add_outgoing_edge(self)
        self.to_node = to_node
        self.to_node.add_incoming_edge(self)

        self._default_edge_color = self._COLOR_BLACK
        if edge_color is not None:
            self._default_edge_color = edge_color

        self._default_text_color = self._COLOR_BLACK
        self._default_color = self._COLOR_BLACK
        self._text_brush = QBrush(self._default_color)
        self._shape_brush = QBrush(self._default_color)
        if style in ['dashed', 'dotted']:
            self._shape_brush = QBrush(Qt.transparent)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_text_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._edge_pen = QPen(self._label_pen)
        self._edge_pen.setWidth(penwidth)
        self._edge_pen.setColor(self._default_edge_color)
        self._edge_pen.setStyle(self._qt_pen_styles.get(style, Qt.SolidLine))

        self._sibling_edges = set()

        self._label = None
        if label is not None:
            self._label = QGraphicsSimpleTextItem(label)
            label_rect = self._label.boundingRect()
            label_rect.moveCenter(label_center)
            self._label.setPos(label_rect.x(), label_rect.y())
            self._label.hoverEnterEvent = self._handle_hoverEnterEvent
            self._label.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._label.setAcceptHoverEvents(True)

        # spline specification according to http://www.graphviz.org/doc/info/attrs.html#k:splineType
        coordinates = spline.split(' ')
        # extract optional end_point
        end_point = None
        if (coordinates[0].startswith('e,')):
            parts = coordinates.pop(0)[2:].split(',')
            end_point = QPointF(float(parts[0]), -float(parts[1]))
        # extract optional start_point
        if (coordinates[0].startswith('s,')):
            parts = coordinates.pop(0).split(',')

        # first point
        parts = coordinates.pop(0).split(',')
        point = QPointF(float(parts[0]), -float(parts[1]))
        path = QPainterPath(point)

        while len(coordinates) > 2:
            # extract triple of points for a cubic spline
            parts = coordinates.pop(0).split(',')
            point1 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point2 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point3 = QPointF(float(parts[0]), -float(parts[1]))
            path.cubicTo(point1, point2, point3)

        self._arrow = None
        if end_point is not None:
            # draw arrow
            self._arrow = QGraphicsPolygonItem()
            polygon = QPolygonF()
            polygon.append(point3)
            offset = QPointF(end_point - point3)
            corner1 = QPointF(-offset.y(), offset.x()) * 0.35
            corner2 = QPointF(offset.y(), -offset.x()) * 0.35
            polygon.append(point3 + corner1)
            polygon.append(end_point)
            polygon.append(point3 + corner2)
            self._arrow.setPolygon(polygon)
            self._arrow.hoverEnterEvent = self._handle_hoverEnterEvent
            self._arrow.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._arrow.setAcceptHoverEvents(True)

        self._path = QGraphicsPathItem()
        self._path.setPath(path)
        self.addToGroup(self._path)

        self.set_node_color()
        self.set_label_color()
Example #8
0
class EdgeItem(GraphItem):

    _qt_pen_styles = {
        'dashed': Qt.DashLine,
        'dotted': Qt.DotLine,
        'solid': Qt.SolidLine,
    }

    def __init__(self, highlight_level, spline, label_center, label, from_node, to_node, parent=None, penwidth=1, edge_color=None, style='solid'):
        super(EdgeItem, self).__init__(highlight_level, parent)

        self.from_node = from_node
        self.from_node.add_outgoing_edge(self)
        self.to_node = to_node
        self.to_node.add_incoming_edge(self)

        self._default_edge_color = self._COLOR_BLACK
        if edge_color is not None:
            self._default_edge_color = edge_color

        self._default_text_color = self._COLOR_BLACK
        self._default_color = self._COLOR_BLACK
        self._text_brush = QBrush(self._default_color)
        self._shape_brush = QBrush(self._default_color)
        if style in ['dashed', 'dotted']:
            self._shape_brush = QBrush(Qt.transparent)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_text_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._edge_pen = QPen(self._label_pen)
        self._edge_pen.setWidth(penwidth)
        self._edge_pen.setColor(self._default_edge_color)
        self._edge_pen.setStyle(self._qt_pen_styles.get(style, Qt.SolidLine))

        self._sibling_edges = set()

        self._label = None
        if label is not None:
            self._label = QGraphicsSimpleTextItem(label)
            label_rect = self._label.boundingRect()
            label_rect.moveCenter(label_center)
            self._label.setPos(label_rect.x(), label_rect.y())
            self._label.hoverEnterEvent = self._handle_hoverEnterEvent
            self._label.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._label.setAcceptHoverEvents(True)

        # spline specification according to http://www.graphviz.org/doc/info/attrs.html#k:splineType
        coordinates = spline.split(' ')
        # extract optional end_point
        end_point = None
        if (coordinates[0].startswith('e,')):
            parts = coordinates.pop(0)[2:].split(',')
            end_point = QPointF(float(parts[0]), -float(parts[1]))
        # extract optional start_point
        if (coordinates[0].startswith('s,')):
            parts = coordinates.pop(0).split(',')

        # first point
        parts = coordinates.pop(0).split(',')
        point = QPointF(float(parts[0]), -float(parts[1]))
        path = QPainterPath(point)

        while len(coordinates) > 2:
            # extract triple of points for a cubic spline
            parts = coordinates.pop(0).split(',')
            point1 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point2 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point3 = QPointF(float(parts[0]), -float(parts[1]))
            path.cubicTo(point1, point2, point3)

        self._arrow = None
        if end_point is not None:
            # draw arrow
            self._arrow = QGraphicsPolygonItem()
            polygon = QPolygonF()
            polygon.append(point3)
            offset = QPointF(end_point - point3)
            corner1 = QPointF(-offset.y(), offset.x()) * 0.35
            corner2 = QPointF(offset.y(), -offset.x()) * 0.35
            polygon.append(point3 + corner1)
            polygon.append(end_point)
            polygon.append(point3 + corner2)
            self._arrow.setPolygon(polygon)
            self._arrow.hoverEnterEvent = self._handle_hoverEnterEvent
            self._arrow.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._arrow.setAcceptHoverEvents(True)

        self._path = QGraphicsPathItem()
        self._path.setPath(path)
        self.addToGroup(self._path)

        self.set_node_color()
        self.set_label_color()

    def add_to_scene(self, scene):
        scene.addItem(self)
        if self._label is not None:
            scene.addItem(self._label)
        if self._arrow is not None:
            scene.addItem(self._arrow)

    def setToolTip(self, tool_tip):
        super(EdgeItem, self).setToolTip(tool_tip)
        if self._label is not None:
            self._label.setToolTip(tool_tip)
        if self._arrow is not None:
            self._arrow.setToolTip(tool_tip)

    def add_sibling_edge(self, edge):
        self._sibling_edges.add(edge)

    def set_node_color(self, color=None):
        if color is None:
            self._label_pen.setColor(self._default_text_color)
            self._text_brush.setColor(self._default_color)
            if self._shape_brush.isOpaque():
                self._shape_brush.setColor(self._default_color)
            self._edge_pen.setColor(self._default_edge_color)
        else:
            self._label_pen.setColor(color)
            self._text_brush.setColor(color)
            if self._shape_brush.isOpaque():
                self._shape_brush.setColor(color)
            self._edge_pen.setColor(color)

        self._path.setPen(self._edge_pen)
        if self._arrow is not None:
            self._arrow.setBrush(self._shape_brush)
            self._arrow.setPen(self._edge_pen)

    def set_label_color(self, color=None):
        if color is None:
            self._label_pen.setColor(self._default_text_color)
        else:
            self._label_pen.setColor(color)

        if self._label is not None:
            self._label.setBrush(self._text_brush)
            self._label.setPen(self._label_pen)

    def _handle_hoverEnterEvent(self, event):
        # hovered edge item in red
        self.set_node_color(self._COLOR_RED)

        if self._highlight_level > 1:
            if self.from_node != self.to_node:
                # from-node in blue
                self.from_node.set_node_color(self._COLOR_BLUE)
                # to-node in green
                self.to_node.set_node_color(self._COLOR_GREEN)
            else:
                # from-node/in-node in teal
                self.from_node.set_node_color(self._COLOR_TEAL)
                self.to_node.set_node_color(self._COLOR_TEAL)
        if self._highlight_level > 2:
            # sibling edges in orange
            for sibling_edge in self._sibling_edges:
                sibling_edge.set_node_color(self._COLOR_ORANGE)

    def _handle_hoverLeaveEvent(self, event):
        self.set_node_color()
        if self._highlight_level > 1:
            self.from_node.set_node_color()
            self.to_node.set_node_color()
        if self._highlight_level > 2:
            for sibling_edge in self._sibling_edges:
                sibling_edge.set_node_color()
Example #9
0
    def __init__(self,
                 highlight_level,
                 spline,
                 label_center,
                 label,
                 from_node,
                 to_node,
                 parent=None,
                 penwidth=1,
                 edge_color=None,
                 style='solid'):
        super(EdgeItem, self).__init__(highlight_level, parent)

        self.from_node = from_node
        self.from_node.add_outgoing_edge(self)
        self.to_node = to_node
        self.to_node.add_incoming_edge(self)

        self._default_edge_color = self._COLOR_BLACK
        if edge_color is not None:
            self._default_edge_color = edge_color

        self._default_text_color = self._COLOR_BLACK
        self._default_color = self._COLOR_BLACK
        self._text_brush = QBrush(self._default_color)
        self._shape_brush = QBrush(self._default_color)
        if style in ['dashed', 'dotted']:
            self._shape_brush = QBrush(Qt.transparent)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_text_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._edge_pen = QPen(self._label_pen)
        self._edge_pen.setWidth(penwidth)
        self._edge_pen.setColor(self._default_edge_color)
        self._edge_pen.setStyle(self._qt_pen_styles.get(style, Qt.SolidLine))

        self._sibling_edges = set()

        self._label = None
        if label is not None:
            self._label = QGraphicsSimpleTextItem(label)
            label_rect = self._label.boundingRect()
            label_rect.moveCenter(label_center)
            self._label.setPos(label_rect.x(), label_rect.y())
            self._label.hoverEnterEvent = self._handle_hoverEnterEvent
            self._label.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._label.setAcceptHoverEvents(True)

        # spline specification according to http://www.graphviz.org/doc/info/attrs.html#k:splineType
        coordinates = spline.split(' ')
        # extract optional end_point
        end_point = None
        if (coordinates[0].startswith('e,')):
            parts = coordinates.pop(0)[2:].split(',')
            end_point = QPointF(float(parts[0]), -float(parts[1]))
        # extract optional start_point
        if (coordinates[0].startswith('s,')):
            parts = coordinates.pop(0).split(',')

        # first point
        parts = coordinates.pop(0).split(',')
        point = QPointF(float(parts[0]), -float(parts[1]))
        path = QPainterPath(point)

        while len(coordinates) > 2:
            # extract triple of points for a cubic spline
            parts = coordinates.pop(0).split(',')
            point1 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point2 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point3 = QPointF(float(parts[0]), -float(parts[1]))
            path.cubicTo(point1, point2, point3)

        self._arrow = None
        if end_point is not None:
            # draw arrow
            self._arrow = QGraphicsPolygonItem()
            polygon = QPolygonF()
            polygon.append(point3)
            offset = QPointF(end_point - point3)
            corner1 = QPointF(-offset.y(), offset.x()) * 0.35
            corner2 = QPointF(offset.y(), -offset.x()) * 0.35
            polygon.append(point3 + corner1)
            polygon.append(end_point)
            polygon.append(point3 + corner2)
            self._arrow.setPolygon(polygon)
            self._arrow.hoverEnterEvent = self._handle_hoverEnterEvent
            self._arrow.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._arrow.setAcceptHoverEvents(True)

        self._path = QGraphicsPathItem()
        self._path.setPath(path)
        self.addToGroup(self._path)

        self.set_node_color()
        self.set_label_color()
Example #10
0
class EdgeItem(GraphItem):

    _qt_pen_styles = {
        'dashed': Qt.DashLine,
        'dotted': Qt.DotLine,
        'solid': Qt.SolidLine,
    }

    def __init__(self,
                 highlight_level,
                 spline,
                 label_center,
                 label,
                 from_node,
                 to_node,
                 parent=None,
                 penwidth=1,
                 edge_color=None,
                 style='solid'):
        super(EdgeItem, self).__init__(highlight_level, parent)

        self.from_node = from_node
        self.from_node.add_outgoing_edge(self)
        self.to_node = to_node
        self.to_node.add_incoming_edge(self)

        self._default_edge_color = self._COLOR_BLACK
        if edge_color is not None:
            self._default_edge_color = edge_color

        self._default_text_color = self._COLOR_BLACK
        self._default_color = self._COLOR_BLACK
        self._text_brush = QBrush(self._default_color)
        self._shape_brush = QBrush(self._default_color)
        if style in ['dashed', 'dotted']:
            self._shape_brush = QBrush(Qt.transparent)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_text_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._edge_pen = QPen(self._label_pen)
        self._edge_pen.setWidth(penwidth)
        self._edge_pen.setColor(self._default_edge_color)
        self._edge_pen.setStyle(self._qt_pen_styles.get(style, Qt.SolidLine))

        self._sibling_edges = set()

        self._label = None
        if label is not None:
            self._label = QGraphicsSimpleTextItem(label)
            label_rect = self._label.boundingRect()
            label_rect.moveCenter(label_center)
            self._label.setPos(label_rect.x(), label_rect.y())
            self._label.hoverEnterEvent = self._handle_hoverEnterEvent
            self._label.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._label.setAcceptHoverEvents(True)

        # spline specification according to http://www.graphviz.org/doc/info/attrs.html#k:splineType
        coordinates = spline.split(' ')
        # extract optional end_point
        end_point = None
        if (coordinates[0].startswith('e,')):
            parts = coordinates.pop(0)[2:].split(',')
            end_point = QPointF(float(parts[0]), -float(parts[1]))
        # extract optional start_point
        if (coordinates[0].startswith('s,')):
            parts = coordinates.pop(0).split(',')

        # first point
        parts = coordinates.pop(0).split(',')
        point = QPointF(float(parts[0]), -float(parts[1]))
        path = QPainterPath(point)

        while len(coordinates) > 2:
            # extract triple of points for a cubic spline
            parts = coordinates.pop(0).split(',')
            point1 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point2 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point3 = QPointF(float(parts[0]), -float(parts[1]))
            path.cubicTo(point1, point2, point3)

        self._arrow = None
        if end_point is not None:
            # draw arrow
            self._arrow = QGraphicsPolygonItem()
            polygon = QPolygonF()
            polygon.append(point3)
            offset = QPointF(end_point - point3)
            corner1 = QPointF(-offset.y(), offset.x()) * 0.35
            corner2 = QPointF(offset.y(), -offset.x()) * 0.35
            polygon.append(point3 + corner1)
            polygon.append(end_point)
            polygon.append(point3 + corner2)
            self._arrow.setPolygon(polygon)
            self._arrow.hoverEnterEvent = self._handle_hoverEnterEvent
            self._arrow.hoverLeaveEvent = self._handle_hoverLeaveEvent
            self._arrow.setAcceptHoverEvents(True)

        self._path = QGraphicsPathItem()
        self._path.setPath(path)
        self.addToGroup(self._path)

        self.set_node_color()
        self.set_label_color()

    def add_to_scene(self, scene):
        scene.addItem(self)
        if self._label is not None:
            scene.addItem(self._label)
        if self._arrow is not None:
            scene.addItem(self._arrow)

    def setToolTip(self, tool_tip):
        super(EdgeItem, self).setToolTip(tool_tip)
        if self._label is not None:
            self._label.setToolTip(tool_tip)
        if self._arrow is not None:
            self._arrow.setToolTip(tool_tip)

    def add_sibling_edge(self, edge):
        self._sibling_edges.add(edge)

    def set_node_color(self, color=None):
        if color is None:
            self._label_pen.setColor(self._default_text_color)
            self._text_brush.setColor(self._default_color)
            if self._shape_brush.isOpaque():
                self._shape_brush.setColor(self._default_color)
            self._edge_pen.setColor(self._default_edge_color)
        else:
            self._label_pen.setColor(color)
            self._text_brush.setColor(color)
            if self._shape_brush.isOpaque():
                self._shape_brush.setColor(color)
            self._edge_pen.setColor(color)

        self._path.setPen(self._edge_pen)
        if self._arrow is not None:
            self._arrow.setBrush(self._shape_brush)
            self._arrow.setPen(self._edge_pen)

    def set_label_color(self, color=None):
        if color is None:
            self._label_pen.setColor(self._default_text_color)
        else:
            self._label_pen.setColor(color)

        if self._label is not None:
            self._label.setBrush(self._text_brush)
            self._label.setPen(self._label_pen)

    def _handle_hoverEnterEvent(self, event):
        # hovered edge item in red
        self.set_node_color(self._COLOR_RED)

        if self._highlight_level > 1:
            if self.from_node != self.to_node:
                # from-node in blue
                self.from_node.set_node_color(self._COLOR_BLUE)
                # to-node in green
                self.to_node.set_node_color(self._COLOR_GREEN)
            else:
                # from-node/in-node in teal
                self.from_node.set_node_color(self._COLOR_TEAL)
                self.to_node.set_node_color(self._COLOR_TEAL)
        if self._highlight_level > 2:
            # sibling edges in orange
            for sibling_edge in self._sibling_edges:
                sibling_edge.set_node_color(self._COLOR_ORANGE)

    def _handle_hoverLeaveEvent(self, event):
        self.set_node_color()
        if self._highlight_level > 1:
            self.from_node.set_node_color()
            self.to_node.set_node_color()
        if self._highlight_level > 2:
            for sibling_edge in self._sibling_edges:
                sibling_edge.set_node_color()
Example #11
0
class NodeItem(GraphItem):

    def __init__(self, highlight_level, bounding_box, label, shape, color=None, parent=None, label_pos=None, tooltip=None):
        super(NodeItem, self).__init__(highlight_level, parent)

        self._default_color = self._COLOR_BLACK if color is None else color
        self._brush = QBrush(self._default_color)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._ellipse_pen = QPen(self._label_pen)
        self._ellipse_pen.setWidth(1)

        self._incoming_edges = set()
        self._outgoing_edges = set()

        if shape == 'box':
            self._graphics_item = QGraphicsRectItem(bounding_box)
        else:
            self._graphics_item = QGraphicsEllipseItem(bounding_box)
        self.addToGroup(self._graphics_item)

        self._label = QGraphicsSimpleTextItem(label)
        label_rect = self._label.boundingRect()
        if label_pos is None:
            label_rect.moveCenter(bounding_box.center())
        else:
            label_rect.moveCenter(label_pos)
        self._label.setPos(label_rect.x(), label_rect.y())
        self.addToGroup(self._label)
        if tooltip is not None:
            self.setToolTip(tooltip)

        self.set_node_color()

        self.setAcceptHoverEvents(True)

        self.hovershape = None

    def set_hovershape(self, newhovershape):
        self.hovershape = newhovershape

    def shape(self):
        if self.hovershape is not None:
            path = QPainterPath()
            path.addRect(self.hovershape)
            return path
        else:
            return super(self.__class__, self).shape()

    def add_incoming_edge(self, edge):
        self._incoming_edges.add(edge)

    def add_outgoing_edge(self, edge):
        self._outgoing_edges.add(edge)

    def set_node_color(self, color=None):
        if color is None:
            color = self._default_color

        self._brush.setColor(color)
        self._ellipse_pen.setColor(color)
        self._label_pen.setColor(color)

        self._graphics_item.setPen(self._ellipse_pen)
        self._label.setBrush(self._brush)
        self._label.setPen(self._label_pen)

    def hoverEnterEvent(self, event):
        # hovered node item in red
        self.set_node_color(self._COLOR_RED)

        if self._highlight_level > 1:
            cyclic_edges = self._incoming_edges.intersection(self._outgoing_edges)
            # incoming edges in blue
            incoming_nodes = set()
            for incoming_edge in self._incoming_edges.difference(cyclic_edges):
                incoming_edge.set_node_color(self._COLOR_BLUE)
                if incoming_edge.from_node != self:
                    incoming_nodes.add(incoming_edge.from_node)
            # outgoing edges in green
            outgoing_nodes = set()
            for outgoing_edge in self._outgoing_edges.difference(cyclic_edges):
                outgoing_edge.set_node_color(self._COLOR_GREEN)
                if outgoing_edge.to_node != self:
                    outgoing_nodes.add(outgoing_edge.to_node)
            # incoming/outgoing edges in teal
            for edge in cyclic_edges:
                edge.set_node_color(self._COLOR_TEAL)

            if self._highlight_level > 2:
                cyclic_nodes = incoming_nodes.intersection(outgoing_nodes)
                # incoming nodes in blue
                for incoming_node in incoming_nodes.difference(cyclic_nodes):
                    incoming_node.set_node_color(self._COLOR_BLUE)
                # outgoing nodes in green
                for outgoing_node in outgoing_nodes.difference(cyclic_nodes):
                    outgoing_node.set_node_color(self._COLOR_GREEN)
                # incoming/outgoing nodes in teal
                for node in cyclic_nodes:
                    node.set_node_color(self._COLOR_TEAL)

    def hoverLeaveEvent(self, event):
        self.set_node_color()
        if self._highlight_level > 1:
            for incoming_edge in self._incoming_edges:
                incoming_edge.set_node_color()
                if self._highlight_level > 2 and incoming_edge.from_node != self:
                    incoming_edge.from_node.set_node_color()
            for outgoing_edge in self._outgoing_edges:
                outgoing_edge.set_node_color()
                if self._highlight_level > 2 and outgoing_edge.to_node != self:
                    outgoing_edge.to_node.set_node_color()
Example #12
0
    def __init__(self, spline, label, label_center, from_node, to_node, parent=None, **kwargs):
        super(EdgeItem, self).__init__(parent, **kwargs)

        self._edge_pen_width = kwargs.get('edge_pen_width', self.EDGE_PEN_WIDTH)

        self.from_node = from_node
        self.from_node.add_outgoing_edge(self)

        self.to_node = to_node
        self.to_node.add_incoming_edge(self)

        self._brush = QBrush(self._color)

        self._label_pen = QPen()
        self._label_pen.setColor(self._color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._label_pen.setWidthF(self._label_pen_width)

        self._edge_pen = QPen()
        self._edge_pen.setColor(self._color)
        self._edge_pen.setWidthF(self._edge_pen_width)

        self._sibling_edges = set()

        self._label = None
        if label is not None:
            self._label = QGraphicsSimpleTextItem(label)

            font = self._label.font()
            font.setPointSize(8)
            self._label.setFont(font)

            label_rect = self._label.boundingRect()
            label_rect.moveCenter(label_center)
            self._label.setPos(label_rect.x(), label_rect.y())

        # spline specification according to http://www.graphviz.org/doc/info/attrs.html#k:splineType
        coordinates = spline.split(' ')
        # extract optional end_point
        end_point = None
        if coordinates[0].startswith('e,'):
            parts = coordinates.pop(0)[2:].split(',')
            end_point = QPointF(float(parts[0]), -float(parts[1]))
        # extract optional start_point
        if coordinates[0].startswith('s,'):
            parts = coordinates.pop(0).split(',')

        # first point
        parts = coordinates.pop(0).split(',')
        point = QPointF(float(parts[0]), -float(parts[1]))
        path = QPainterPath(point)

        while len(coordinates) > 2:
            # extract triple of points for a cubic spline
            parts = coordinates.pop(0).split(',')
            point1 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point2 = QPointF(float(parts[0]), -float(parts[1]))
            parts = coordinates.pop(0).split(',')
            point3 = QPointF(float(parts[0]), -float(parts[1]))
            path.cubicTo(point1, point2, point3)

        self._arrow = None
        if end_point is not None:
            # draw arrow
            self._arrow = QGraphicsPolygonItem()
            polygon = QPolygonF()
            polygon.append(point3)
            offset = QPointF(end_point - point3)
            corner1 = QPointF(-offset.y(), offset.x()) * 0.35
            corner2 = QPointF(offset.y(), -offset.x()) * 0.35
            polygon.append(point3 + corner1)
            polygon.append(end_point)
            polygon.append(point3 + corner2)
            self._arrow.setPolygon(polygon)

        self._path = QGraphicsPathItem()
        self._path.setPath(path)
        self.addToGroup(self._path)

        self._brush.setColor(self._color)
        self._edge_pen.setColor(self._color)
        self._label_pen.setColor(self._color)

        self._path.setPen(self._edge_pen)
        if self._arrow is not None:
            self._arrow.setBrush(self._brush)
            self._arrow.setPen(self._edge_pen)
        if self._label is not None:
            self._label.setBrush(self._brush)
            self._label.setPen(self._label_pen)
Example #13
0
    def __init__(self, highlight_level, bounding_box, label, shape, color=None, parent=None, label_pos=None, tooltip=None):
        super(NodeItem, self).__init__(highlight_level, parent)

        self._default_color = self._COLOR_BLACK if color is None else color
        self._brush = QBrush(self._default_color)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._ellipse_pen = QPen(self._label_pen)
        self._ellipse_pen.setWidth(1)

        self._incoming_edges = set()
        self._outgoing_edges = set()

        if shape == 'box':
            self._graphics_item = QGraphicsRectItem(bounding_box)

        # Since we don't have unique GraphicsItems other than Ellipse and Rect,
        # Using Polygon to draw the following using bounding_box

        elif shape == 'octagon':
            rect = bounding_box.getRect()
            octagon_polygon = QPolygonF([QPointF(rect[0], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + rect[2], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + rect[2], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1]),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1])])
            self._graphics_item = QGraphicsPolygonItem(octagon_polygon)

        elif shape == 'doubleoctagon':
            rect = bounding_box.getRect()
            inner_fold = 3.0

            octagon_polygon = QPolygonF([QPointF(rect[0], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + rect[2], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + rect[2], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1]),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1]),
                                         # inner
                                         QPointF(rect[0], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0] + inner_fold, rect[1] + 3 * rect[3] / 10 + inner_fold / 2),
                                         QPointF(rect[0] + inner_fold, rect[1] + 7 * rect[3] / 10 - inner_fold / 2),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + rect[3] - inner_fold),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + rect[3] - inner_fold),
                                         QPointF(rect[0] + rect[2] - inner_fold, rect[1] + 7 * rect[3] / 10 - inner_fold / 2),
                                         QPointF(rect[0] + rect[2] - inner_fold, rect[1] + 3 * rect[3] / 10 + inner_fold / 2),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + inner_fold),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + inner_fold),
                                         QPointF(rect[0] + inner_fold, rect[1] + 3 * rect[3] / 10 + inner_fold / 2)
                                         ])

            self._graphics_item = QGraphicsPolygonItem(octagon_polygon)

        elif shape == 'note':
            rect = bounding_box.getRect()
            note_polygon = QPolygonF([QPointF(rect[0] + 9 * rect[2] / 10, rect[1]),
                                      QPointF(rect[0], rect[1]),
                                      QPointF(rect[0], rect[1] + rect[3]),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3]),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3] / 5),
                                      QPointF(rect[0] + 9 * rect[2] / 10, rect[1] + rect[3] / 5),
                                      QPointF(rect[0] + 9 * rect[2] / 10, rect[1]),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3] / 5),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3] / 5)])
            self._graphics_item = QGraphicsPolygonItem(note_polygon)

        else:
            self._graphics_item = QGraphicsEllipseItem(bounding_box)
        self.addToGroup(self._graphics_item)

        self._label = QGraphicsSimpleTextItem(label)
        label_rect = self._label.boundingRect()
        if label_pos is None:
            label_rect.moveCenter(bounding_box.center())
        else:
            label_rect.moveCenter(label_pos)
        self._label.setPos(label_rect.x(), label_rect.y())
        self.addToGroup(self._label)
        if tooltip is not None:
            self.setToolTip(tooltip)

        self.set_node_color()

        self.setAcceptHoverEvents(True)

        self.hovershape = None
Example #14
0
class NodeItem(GraphItem):

    def __init__(self, highlight_level, bounding_box, label, shape, color=None, parent=None, label_pos=None, tooltip=None):
        super(NodeItem, self).__init__(highlight_level, parent)

        self._default_color = self._COLOR_BLACK if color is None else color
        self._brush = QBrush(self._default_color)
        self._label_pen = QPen()
        self._label_pen.setColor(self._default_color)
        self._label_pen.setJoinStyle(Qt.RoundJoin)
        self._ellipse_pen = QPen(self._label_pen)
        self._ellipse_pen.setWidth(1)

        self._incoming_edges = set()
        self._outgoing_edges = set()

        if shape == 'box':
            self._graphics_item = QGraphicsRectItem(bounding_box)

        # Since we don't have unique GraphicsItems other than Ellipse and Rect,
        # Using Polygon to draw the following using bounding_box

        elif shape == 'octagon':
            rect = bounding_box.getRect()
            octagon_polygon = QPolygonF([QPointF(rect[0], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + rect[2], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + rect[2], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1]),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1])])
            self._graphics_item = QGraphicsPolygonItem(octagon_polygon)

        elif shape == 'doubleoctagon':
            rect = bounding_box.getRect()
            inner_fold = 3.0

            octagon_polygon = QPolygonF([QPointF(rect[0], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + rect[3]),
                                         QPointF(rect[0] + rect[2], rect[1] + 7 * rect[3] / 10),
                                         QPointF(rect[0] + rect[2], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1]),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1]),
                                         # inner
                                         QPointF(rect[0], rect[1] + 3 * rect[3] / 10),
                                         QPointF(rect[0] + inner_fold, rect[1] + 3 * rect[3] / 10 + inner_fold / 2),
                                         QPointF(rect[0] + inner_fold, rect[1] + 7 * rect[3] / 10 - inner_fold / 2),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + rect[3] - inner_fold),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + rect[3] - inner_fold),
                                         QPointF(rect[0] + rect[2] - inner_fold, rect[1] + 7 * rect[3] / 10 - inner_fold / 2),
                                         QPointF(rect[0] + rect[2] - inner_fold, rect[1] + 3 * rect[3] / 10 + inner_fold / 2),
                                         QPointF(rect[0] + 7 * rect[2] / 10, rect[1] + inner_fold),
                                         QPointF(rect[0] + 3 * rect[2] / 10, rect[1] + inner_fold),
                                         QPointF(rect[0] + inner_fold, rect[1] + 3 * rect[3] / 10 + inner_fold / 2)
                                         ])

            self._graphics_item = QGraphicsPolygonItem(octagon_polygon)

        elif shape == 'note':
            rect = bounding_box.getRect()
            note_polygon = QPolygonF([QPointF(rect[0] + 9 * rect[2] / 10, rect[1]),
                                      QPointF(rect[0], rect[1]),
                                      QPointF(rect[0], rect[1] + rect[3]),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3]),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3] / 5),
                                      QPointF(rect[0] + 9 * rect[2] / 10, rect[1] + rect[3] / 5),
                                      QPointF(rect[0] + 9 * rect[2] / 10, rect[1]),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3] / 5),
                                      QPointF(rect[0] + rect[2], rect[1] + rect[3] / 5)])
            self._graphics_item = QGraphicsPolygonItem(note_polygon)

        else:
            self._graphics_item = QGraphicsEllipseItem(bounding_box)
        self.addToGroup(self._graphics_item)

        self._label = QGraphicsSimpleTextItem(label)
        label_rect = self._label.boundingRect()
        if label_pos is None:
            label_rect.moveCenter(bounding_box.center())
        else:
            label_rect.moveCenter(label_pos)
        self._label.setPos(label_rect.x(), label_rect.y())
        self.addToGroup(self._label)
        if tooltip is not None:
            self.setToolTip(tooltip)

        self.set_node_color()

        self.setAcceptHoverEvents(True)

        self.hovershape = None

    def set_hovershape(self, newhovershape):
        self.hovershape = newhovershape

    def shape(self):
        if self.hovershape is not None:
            path = QPainterPath()
            path.addRect(self.hovershape)
            return path
        else:
            return super(self.__class__, self).shape()

    def add_incoming_edge(self, edge):
        self._incoming_edges.add(edge)

    def add_outgoing_edge(self, edge):
        self._outgoing_edges.add(edge)

    def set_node_color(self, color=None):
        if color is None:
            color = self._default_color

        self._brush.setColor(color)
        self._ellipse_pen.setColor(color)
        self._label_pen.setColor(color)

        self._graphics_item.setPen(self._ellipse_pen)
        self._label.setBrush(self._brush)
        self._label.setPen(self._label_pen)

    def hoverEnterEvent(self, event):
        # hovered node item in red
        self.set_node_color(self._COLOR_RED)

        if self._highlight_level > 1:
            cyclic_edges = self._incoming_edges.intersection(self._outgoing_edges)
            # incoming edges in blue
            incoming_nodes = set()
            for incoming_edge in self._incoming_edges.difference(cyclic_edges):
                incoming_edge.set_node_color(self._COLOR_BLUE)
                if incoming_edge.from_node != self:
                    incoming_nodes.add(incoming_edge.from_node)
            # outgoing edges in green
            outgoing_nodes = set()
            for outgoing_edge in self._outgoing_edges.difference(cyclic_edges):
                outgoing_edge.set_node_color(self._COLOR_GREEN)
                if outgoing_edge.to_node != self:
                    outgoing_nodes.add(outgoing_edge.to_node)
            # incoming/outgoing edges in teal
            for edge in cyclic_edges:
                edge.set_node_color(self._COLOR_TEAL)

            if self._highlight_level > 2:
                cyclic_nodes = incoming_nodes.intersection(outgoing_nodes)
                # incoming nodes in blue
                for incoming_node in incoming_nodes.difference(cyclic_nodes):
                    incoming_node.set_node_color(self._COLOR_BLUE)
                # outgoing nodes in green
                for outgoing_node in outgoing_nodes.difference(cyclic_nodes):
                    outgoing_node.set_node_color(self._COLOR_GREEN)
                # incoming/outgoing nodes in teal
                for node in cyclic_nodes:
                    node.set_node_color(self._COLOR_TEAL)

    def hoverLeaveEvent(self, event):
        self.set_node_color()
        if self._highlight_level > 1:
            for incoming_edge in self._incoming_edges:
                incoming_edge.set_node_color()
                if self._highlight_level > 2 and incoming_edge.from_node != self:
                    incoming_edge.from_node.set_node_color()
            for outgoing_edge in self._outgoing_edges:
                outgoing_edge.set_node_color()
                if self._highlight_level > 2 and outgoing_edge.to_node != self:
                    outgoing_edge.to_node.set_node_color()
class TimelineClip(QGraphicsRectItem):
    _delta_duration = 0.005
    _z = 1.0
    _precision = 3

    def __init__(self, track, text, starttime, duration):
        self._track = track
        super(TimelineClip, self).__init__(0.0, 0.0, duration, track.height, None, track.scene())
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setZValue(self._z)
        self.setBrush(QBrush(self._track._colors['item']))
        self.setPen(QPen(self._track._colors['item']))

        self._text = QGraphicsSimpleTextItem(text, self)
        self._text.setBrush(QColor(Qt.black))
        self._text.setFont(QFont('sans', 12))
        self._text.setPos(0.05, 0.05)
        self._text.setFlag(QGraphicsItem.ItemIgnoresTransformations)

        self.set_starttime(starttime)
        self.set_data([])


    def remove(self):
        self.scene().removeItem(self)


    def text(self):
        return self._text.text()

    def set_text(self, text):
        self._text.setText(text)


    def starttime(self):
        return self.pos().x()

    def set_starttime(self, starttime):
        self.setPos(starttime, self._track._y)


    def duration(self):
        return self.rect().width()

    def set_duration(self, duration):
        rect = self.rect()
        rect.setWidth(duration)
        self.setRect(rect)


    def endtime(self):
        return self.starttime() + self.duration()

    def set_endtime(self, endtime):
        self.set_duration(endtime - self.starttime)


    def data(self):
        return self._data

    def set_data(self, data):
        self._data = list(data)


    def set_highlight(self, enabled=True):
        if enabled:
            self.setBrush(QBrush(self._track._colors['highlight']))
        else:
            self.setBrush(QBrush(self._track._colors['item']))


    def contextMenuEvent(self, event):
        menu = QMenu()
        clip = menu.addAction('clip')
        clip.setEnabled(False)
        menu.addSeparator()
        remove = menu.addAction('remove clip')
        result = menu.exec_(event.screenPos())
        if result == remove:
            self.scene().removeItem(self)


    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            min_starttime = self._track.starttime
            max_starttime = 1000000000.0
            new_starttime = min(max(min_starttime, round(value.x(), self._precision)), max_starttime)

            # check for track change
            new_track = self.scene().track_at(value.y() + self._track.height / 2)
            if new_track is None or new_track.track_type() != self._track.track_type():
                new_track = self._track

            # prevent overlap with the next clip
            next_clip = new_track.next_clip(new_starttime)
            if next_clip is not None and next_clip is not self:
                max_starttime = next_clip.starttime() - self.duration() # move only up to the next clip
                new_starttime = min(new_starttime, max_starttime)

            # prevent overlap with the previous clip
            previous_clip = new_track.previous_clip(new_starttime)
            if previous_clip is not None and previous_clip is not self:
                min_starttime = previous_clip.endtime() # move only up to the previous clip
                new_starttime = max(new_starttime, min_starttime)

            if min_starttime <= new_starttime <= max_starttime:
                self._track = new_track
                return QPointF(new_starttime, self._track._y)

            return self.pos()

        return super(TimelineClip, self).itemChange(change, value)


    def wheelEvent(self, event):
        event.accept()
        steps = event.delta() / 120.0

        # calculate delta_duration and ensure it is not to small for the precision
        delta_duration = round(steps * self._delta_duration, self._precision)
        if steps > 0:
            delta_duration = max(delta_duration, 0.1 ** self._precision)
        else:
            delta_duration = min(delta_duration, - 0.1 ** self._precision)

        new_duration = round(self.duration() + delta_duration, self._precision)
        starttime = self.starttime()
        next_clip = self._track.next_clip(starttime)
        if next_clip and new_duration > next_clip.starttime() - starttime:
            new_duration = next_clip.starttime() - starttime # stretch only up to the next item

        self.set_duration(new_duration)