diff --git a/NodeGraphQt/qgraphics/pipe.py b/NodeGraphQt/qgraphics/pipe.py index ec5e7b48..bd3cf827 100644 --- a/NodeGraphQt/qgraphics/pipe.py +++ b/NodeGraphQt/qgraphics/pipe.py @@ -416,12 +416,33 @@ def delete(self): class LivePipeItem(PipeItem): + """ + Live Pipe item used for drawing the live connection with the cursor. + """ def __init__(self): super(LivePipeItem, self).__init__() self.setZValue(Z_VAL_NODE_WIDGET + 1) self.shift_selected = False + color = QtGui.QColor(*PipeEnum.ACTIVE_COLOR.value) + pen = QtGui.QPen(color, PipeEnum.WIDTH.value + 0.35) + pen.setJoinStyle(QtCore.Qt.MiterJoin) + pen.setCapStyle(QtCore.Qt.RoundCap) + + self._idx_pointer = LivePipePolygonItem(self) + self._idx_pointer.setPolygon(self._arrow) + self._idx_pointer.setBrush(color.darker(300)) + self._idx_pointer.setPen(pen) + + color = QtGui.QColor(*PipeEnum.ACTIVE_COLOR.value) + color.setAlpha(50) + self._idx_text = QtWidgets.QGraphicsTextItem(self) + self._idx_text.setDefaultTextColor(color) + font = self._idx_text.font() + font.setPointSize(7) + self._idx_text.setFont(font) + def paint(self, painter, option, widget): """ Draws the connection line. @@ -449,21 +470,12 @@ def paint(self, painter, option, widget): cen_y = self.path().pointAtPercent(0.5).y() loc_pt = self.path().pointAtPercent(0.9) tgt_pt = self.path().pointAtPercent(1.0) - start_pt = self.path().pointAtPercent(0.0) dist = math.hypot(tgt_pt.x() - cen_x, tgt_pt.y() - cen_y) if dist < 0.05: painter.restore() return - # draw start circle - size = 5.0 - rect = QtCore.QRectF(start_pt.x() - (size / 2), - start_pt.y() - (size / 2), - size, size) - painter.setBrush(color) - painter.drawEllipse(rect) - # draw middle circle size = 10.0 if dist < 50.0: @@ -490,9 +502,92 @@ def paint(self, painter, option, widget): degrees = math.degrees(radians) + 90 transform.rotate(degrees) - scale = 1.0 - if dist < 20.0: - scale = dist / 20.0 - transform.scale(scale, scale) - painter.drawPolygon(transform.map(self._arrow)) + painter.restore() + + def draw_path(self, start_port, end_port=None, cursor_pos=None, + color_mode=None): + """ + re-implemented to also update the index pointer arrow position. + + Args: + start_port (PortItem): port used to draw the starting point. + end_port (PortItem): port used to draw the end point. + cursor_pos (QtCore.QPointF): cursor position if specified this + will be the draw end point. + color_mode (str): arrow index pointer color mode + ('accept', 'reject' or None). + """ + super(LivePipeItem, self).draw_path(start_port, end_port, cursor_pos) + self.draw_index_pointer(start_port, cursor_pos, color_mode) + + def draw_index_pointer(self, start_port, cursor_pos, color_mode=None): + """ + Update the index pointer arrow position and direction when the + live pipe path is redrawn. + + Args: + start_port (PortItem): start port item. + cursor_pos (QtCore.QPoint): cursor scene position. + color_mode (str): arrow index pointer color mode + ('accept', 'reject' or None). + """ + text_rect = self._idx_text.boundingRect() + + transform = QtGui.QTransform() + transform.translate(cursor_pos.x(), cursor_pos.y()) + if self.viewer_layout_direction() is LayoutDirectionEnum.VERTICAL.value: + text_pos = ( + cursor_pos.x() + (text_rect.width() / 2.5), + cursor_pos.y() - (text_rect.height() / 2) + ) + if start_port.port_type == PortTypeEnum.OUT.value: + transform.rotate(180) + elif self.viewer_layout_direction() is LayoutDirectionEnum.HORIZONTAL.value: + text_pos = ( + cursor_pos.x() - (text_rect.width() / 2), + cursor_pos.y() - (text_rect.height() * 1.25) + ) + if start_port.port_type == PortTypeEnum.IN.value: + transform.rotate(-90) + else: + transform.rotate(90) + self._idx_text.setPos(*text_pos) + self._idx_text.setPlainText('{}'.format(start_port.name)) + + self._idx_pointer.setPolygon(transform.map(self._arrow)) + + if color_mode == 'accept': + color = QtGui.QColor(*PipeEnum.HIGHLIGHT_COLOR.value) + elif color_mode == 'reject': + color = QtGui.QColor(*PipeEnum.DISABLED_COLOR.value) + else: + color = QtGui.QColor(*PipeEnum.ACTIVE_COLOR.value) + + pen = self._idx_pointer.pen() + pen.setColor(color) + self._idx_pointer.setBrush(color.darker(300)) + self._idx_pointer.setPen(pen) + + +class LivePipePolygonItem(QtWidgets.QGraphicsPolygonItem): + """ + Custom live pipe polygon shape. + """ + + def __init__(self, parent): + super(LivePipePolygonItem, self).__init__(parent) + self.setFlag(self.ItemIsSelectable, True) + + def paint(self, painter, option, widget): + """ + Args: + painter (QtGui.QPainter): painter used for drawing the item. + option (QtGui.QStyleOptionGraphicsItem): + used to describe the parameters needed to draw. + widget (QtWidgets.QWidget): not used. + """ + painter.save() + painter.setBrush(self.brush()) + painter.setPen(self.pen()) + painter.drawPolygon(self.polygon()) painter.restore() diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index bd18d7f3..3183c131 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -637,7 +637,7 @@ def keyReleaseEvent(self, event): def sceneMouseMoveEvent(self, event): """ triggered mouse move event for the scene. - - redraw the connection pipe. + - redraw the live connection pipe. Args: event (QtWidgets.QGraphicsSceneMouseEvent): @@ -649,15 +649,27 @@ def sceneMouseMoveEvent(self, event): return pos = event.scenePos() - items = self.scene().items(pos) - if items and isinstance(items[0], PortItem): - x = items[0].boundingRect().width() / 2 - y = items[0].boundingRect().height() / 2 - pos = items[0].scenePos() - pos.setX(pos.x() + x) - pos.setY(pos.y() + y) + color_mode = None + for item in self.scene().items(pos): + if isinstance(item, PortItem): + x = item.boundingRect().width() / 2 + y = item.boundingRect().height() / 2 + pos = item.scenePos() + pos.setX(pos.x() + x) + pos.setY(pos.y() + y) + if item == self._start_port: + break + color_mode = 'accept' + if self.acyclic: + if item.node == self._start_port.node: + color_mode = 'reject' + elif item.port_type == self._start_port.port_type: + color_mode = 'reject' + break - self._LIVE_PIPE.draw_path(self._start_port, cursor_pos=pos) + self._LIVE_PIPE.draw_path( + self._start_port, cursor_pos=pos, color_mode=color_mode + ) def sceneMousePressEvent(self, event): """ @@ -874,6 +886,10 @@ def start_live_connection(self, selected_port): elif self._start_port == PortTypeEnum.OUT.value: self._LIVE_PIPE.output_port = self._start_port self._LIVE_PIPE.setVisible(True) + self._LIVE_PIPE.draw_index_pointer( + selected_port, + self.mapToScene(self._origin_pos) + ) def end_live_connection(self): """