From 9ec4d0a74b856c231ac64d67a59950606fe8a060 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Sat, 24 Aug 2019 16:32:55 +1200 Subject: [PATCH 1/6] doc string tweak --- NodeGraphQt/base/graph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NodeGraphQt/base/graph.py b/NodeGraphQt/base/graph.py index f0d82123..7217ef49 100644 --- a/NodeGraphQt/base/graph.py +++ b/NodeGraphQt/base/graph.py @@ -390,8 +390,8 @@ def set_pipe_style(self, style=None): Set node graph pipes to be drawn straight or curved by default all pipes are set curved. (default=0) - PIPE_LAYOUT_CURVED = 0 - PIPE_LAYOUT_STRAIGHT = 1 + ``NodeGraphQt.constants.PIPE_LAYOUT_CURVED`` = 0 + ``NodeGraphQt.constants.PIPE_LAYOUT_STRAIGHT`` = 1 Args: style (int): pipe style. From 524ec79d65e795a094cef1dbb7774080069e7a40 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Sat, 24 Aug 2019 16:34:37 +1200 Subject: [PATCH 2/6] live pipe connection overhaul clean up. --- NodeGraphQt/qgraphics/pipe.py | 18 +++++- NodeGraphQt/widgets/viewer.py | 115 +++++++++++++++++++++------------- 2 files changed, 88 insertions(+), 45 deletions(-) diff --git a/NodeGraphQt/qgraphics/pipe.py b/NodeGraphQt/qgraphics/pipe.py index a7ca6a97..a032f5a1 100644 --- a/NodeGraphQt/qgraphics/pipe.py +++ b/NodeGraphQt/qgraphics/pipe.py @@ -6,7 +6,8 @@ PIPE_DEFAULT_COLOR, PIPE_ACTIVE_COLOR, PIPE_HIGHLIGHT_COLOR, PIPE_DISABLED_COLOR, PIPE_STYLE_DASHED, PIPE_STYLE_DEFAULT, PIPE_STYLE_DOTTED, - PIPE_LAYOUT_STRAIGHT, PIPE_WIDTH, IN_PORT, OUT_PORT, Z_VAL_PIPE + PIPE_LAYOUT_STRAIGHT, PIPE_WIDTH, IN_PORT, OUT_PORT, Z_VAL_PIPE, + Z_VAL_NODE_WIDGET ) from NodeGraphQt.qgraphics.port import PortItem @@ -130,7 +131,7 @@ def paint(self, painter, option, widget): painter.restore() # QPaintDevice: Cannot destroy paint device that is being painted - def draw_path(self, start_port, end_port, cursor_pos=None): + def draw_path(self, start_port, end_port=None, cursor_pos=None): """ Draws the path between ports. @@ -182,6 +183,10 @@ def draw_path(self, start_port, end_port, cursor_pos=None): path.cubicTo(ctr_point1, ctr_point2, pos2) self.setPath(path) + def reset_path(self): + path = QtGui.QPainterPath(QtCore.QPointF(0.0, 0.0)) + self.setPath(path) + def calc_distance(self, p1, p2): x = math.pow((p2.x() - p1.x()), 2) y = math.pow((p2.y() - p1.y()), 2) @@ -293,3 +298,12 @@ def delete(self): # TODO: not sure if we need this...? del self + +class LivePipe(Pipe): + + def __init__(self, input_port=None, output_port=None): + super(LivePipe, self).__init__(input_port, output_port) + self.setZValue(Z_VAL_NODE_WIDGET + 1) + self.activate() + self.style = PIPE_STYLE_DASHED + self.shift_selected = False diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index e2dc29b0..ff70bbed 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -5,12 +5,10 @@ from NodeGraphQt import QtGui, QtCore, QtWidgets from NodeGraphQt.constants import (IN_PORT, OUT_PORT, PIPE_LAYOUT_CURVED, - PIPE_STYLE_DASHED, - SCENE_AREA, - Z_VAL_NODE_WIDGET) + SCENE_AREA,) from NodeGraphQt.qgraphics.node_abstract import AbstractNodeItem from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem -from NodeGraphQt.qgraphics.pipe import Pipe +from NodeGraphQt.qgraphics.pipe import Pipe, LivePipe from NodeGraphQt.qgraphics.port import PortItem from NodeGraphQt.qgraphics.slicer import SlicerPipe from NodeGraphQt.widgets.scene import NodeScene @@ -55,7 +53,6 @@ def __init__(self, parent=None): self.resize(1000, 800) self._pipe_layout = PIPE_LAYOUT_CURVED - self._live_pipe = None self._detached_port = None self._start_port = None self._origin_pos = None @@ -65,6 +62,11 @@ def __init__(self, parent=None): self._rubber_band = QtWidgets.QRubberBand( QtWidgets.QRubberBand.Rectangle, self ) + + self._live_pipe = LivePipe() + self._live_pipe.setVisible(False) + self.scene().addItem(self._live_pipe) + self._pipe_slicer = SlicerPipe() self._pipe_slicer.setVisible(False) self.scene().addItem(self._pipe_slicer) @@ -87,6 +89,9 @@ def __init__(self, parent=None): self.LMB_state = False self.RMB_state = False self.MMB_state = False + self.ALT_state = False + self.CTRL_state = False + self.SHIFT_state = False def __repr__(self): return '{}.{}()'.format( @@ -123,7 +128,10 @@ def _items_near(self, pos, item_type=None, width=20, height=20): x, y = pos.x() - width, pos.y() - height rect = QtCore.QRectF(x, y, width, height) items = [] + excl = [self._live_pipe, self._pipe_slicer] for item in self.scene().items(rect): + if item in excl: + continue if not item_type or isinstance(item, item_type): items.append(item) return items @@ -135,7 +143,8 @@ def _on_search_submitted(self, node_type): def _on_pipes_sliced(self, path): self.connection_sliced.emit([ [i.input_port, i.output_port] - for i in self.scene().items(path) if isinstance(i, Pipe) + for i in self.scene().items(path) + if isinstance(i, Pipe) and i != self._live_pipe ]) # --- reimplemented events --- @@ -151,15 +160,13 @@ def contextMenuEvent(self, event): return super(NodeViewer, self).contextMenuEvent(event) def mousePressEvent(self, event): - alt_modifier = event.modifiers() == QtCore.Qt.AltModifier - shift_modifier = event.modifiers() == QtCore.Qt.ShiftModifier - if event.button() == QtCore.Qt.LeftButton: self.LMB_state = True elif event.button() == QtCore.Qt.RightButton: self.RMB_state = True elif event.button() == QtCore.Qt.MiddleButton: self.MMB_state = True + self._origin_pos = event.pos() self._previous_pos = event.pos() self._prev_selection = self.selected_nodes() @@ -172,19 +179,21 @@ def mousePressEvent(self, event): map_pos = self.mapToScene(event.pos()) # pipe slicer enabled. - if event.modifiers() == (QtCore.Qt.AltModifier | QtCore.Qt.ShiftModifier): + if self.ALT_state and self.SHIFT_state: self._pipe_slicer.draw_path(map_pos, map_pos) self._pipe_slicer.setVisible(True) return - if alt_modifier: + # pan mode. + if self.ALT_state: return items = self._items_near(map_pos, None, 20, 20) nodes = [i for i in items if isinstance(i, AbstractNodeItem)] + pipes = [i for i in items if isinstance(i, Pipe)] # toggle extend node selection. - if shift_modifier: + if self.SHIFT_state: for node in nodes: node.selected = not node.selected @@ -193,7 +202,7 @@ def mousePressEvent(self, event): {n: n.xy_pos for n in self.selected_nodes()} ) - # show selection selection marquee + # show selection selection marquee. if self.LMB_state and not items: rect = QtCore.QRect(self._previous_pos, QtCore.QSize()) rect = rect.normalized() @@ -202,7 +211,8 @@ def mousePressEvent(self, event): self._rubber_band.setGeometry(rect) self._rubber_band.show() - if not shift_modifier: + # allow new live pipe with the shift modifier. + if not self.SHIFT_state or self.SHIFT_state and pipes: super(NodeViewer, self).mousePressEvent(event) def mouseReleaseEvent(self, event): @@ -241,22 +251,21 @@ def mouseReleaseEvent(self, event): super(NodeViewer, self).mouseReleaseEvent(event) def mouseMoveEvent(self, event): - alt_modifier = event.modifiers() == QtCore.Qt.AltModifier - shift_modifier = event.modifiers() == QtCore.Qt.ShiftModifier - if event.modifiers() == (QtCore.Qt.AltModifier | QtCore.Qt.ShiftModifier): - if self.LMB_state: + if self.ALT_state and self.SHIFT_state: + if self.LMB_state and self._pipe_slicer.isVisible(): p1 = self._pipe_slicer.path().pointAtPercent(0) p2 = self.mapToScene(self._previous_pos) self._pipe_slicer.draw_path(p1, p2) - self._previous_pos = event.pos() - super(NodeViewer, self).mouseMoveEvent(event) - return + self._pipe_slicer.show() + self._previous_pos = event.pos() + super(NodeViewer, self).mouseMoveEvent(event) + return - if self.MMB_state and alt_modifier: + if self.MMB_state and self.ALT_state: pos_x = (event.x() - self._previous_pos.x()) zoom = 0.1 if pos_x > 0 else -0.1 self._set_viewer_zoom(zoom, 0.05) - elif self.MMB_state or (self.LMB_state and alt_modifier): + elif self.MMB_state or (self.LMB_state and self.ALT_state): pos_x = (event.x() - self._previous_pos.x()) pos_y = (event.y() - self._previous_pos.y()) self._set_viewer_pan(pos_x, pos_y) @@ -270,7 +279,7 @@ def mouseMoveEvent(self, event): self.scene().setSelectionArea(path, QtCore.Qt.IntersectsItemShape) self.scene().update(map_rect) - if shift_modifier and self._prev_selection: + if self.SHIFT_state and self._prev_selection: for node in self._prev_selection: if node not in self.selected_nodes(): node.selected = True @@ -309,6 +318,24 @@ def dragMoveEvent(self, event): def dragLeaveEvent(self, event): event.ignore() + def keyPressEvent(self, event): + self.ALT_state = event.modifiers() == QtCore.Qt.AltModifier + self.CTRL_state = event.modifiers() == QtCore.Qt.ControlModifier + self.SHIFT_state = event.modifiers() == QtCore.Qt.ShiftModifier + + # Todo: find a better solution to catch modifier keys. + if event.modifiers() == (QtCore.Qt.AltModifier | QtCore.Qt.ShiftModifier): + self.ALT_state = True + self.SHIFT_state = True + + super(NodeViewer, self).keyPressEvent(event) + + def keyReleaseEvent(self, event): + self.ALT_state = event.modifiers() == QtCore.Qt.AltModifier + self.CTRL_state = event.modifiers() == QtCore.Qt.ControlModifier + self.SHIFT_state = event.modifiers() == QtCore.Qt.ShiftModifier + super(NodeViewer, self).keyReleaseEvent(event) + # --- scene events --- def sceneMouseMoveEvent(self, event): @@ -320,7 +347,7 @@ def sceneMouseMoveEvent(self, event): event (QtWidgets.QGraphicsSceneMouseEvent): The event handler from the QtWidgets.QGraphicsScene """ - if not self._live_pipe: + if not self._live_pipe.isVisible(): return if not self._start_port: return @@ -334,7 +361,7 @@ def sceneMouseMoveEvent(self, event): pos.setX(pos.x() + x) pos.setY(pos.y() + y) - self._live_pipe.draw_path(self._start_port, None, pos) + self._live_pipe.draw_path(self._start_port, cursor_pos=pos) def sceneMousePressEvent(self, event): """ @@ -347,10 +374,10 @@ def sceneMousePressEvent(self, event): The event handler from the QtWidgets.QGraphicsScene """ # pipe slicer enabled. - if event.modifiers() == (QtCore.Qt.AltModifier | QtCore.Qt.ShiftModifier): + if self.ALT_state and self.SHIFT_state: return # viewer pan mode. - if event.modifiers() == QtCore.Qt.AltModifier: + if self.ALT_state: return pos = event.scenePos() @@ -384,13 +411,18 @@ def sceneMousePressEvent(self, event): if not self.LMB_state: return pipe = pipe_items[0] - attr = {IN_PORT: 'output_port', OUT_PORT: 'input_port'} from_port = pipe.port_from_pos(pos, True) from_port._hovered = True + attr = {IN_PORT: 'output_port', OUT_PORT: 'input_port'} self._detached_port = getattr(pipe, attr[from_port.port_type]) self.start_live_connection(from_port) - self._live_pipe.draw_path(self._start_port, None, pos) + self._live_pipe.draw_path(self._start_port, cursor_pos=pos) + + if self.SHIFT_state: + self._live_pipe.shift_selected = True + return + pipe.delete() def sceneMouseReleaseEvent(self, event): @@ -402,7 +434,7 @@ def sceneMouseReleaseEvent(self, event): event (QtWidgets.QGraphicsSceneMouseEvent): The event handler from the QtWidgets.QGraphicsScene """ - if not self._live_pipe: + if not self._live_pipe.isVisible(): return self._start_port._hovered = False @@ -419,7 +451,7 @@ def sceneMouseReleaseEvent(self, event): # if port disconnected from existing pipe. if end_port is None: - if self._detached_port: + if self._detached_port and not self._live_pipe.shift_selected: disconnected.append((self._start_port, self._detached_port)) self.connection_changed.emit(disconnected, connected) @@ -477,29 +509,25 @@ def sceneMouseReleaseEvent(self, event): def start_live_connection(self, selected_port): """ create new pipe for the connection. - (draws the live pipe from the port following the cursor position) + (show the live pipe visibility from the port following the cursor position) """ if not selected_port: return self._start_port = selected_port - self._live_pipe = Pipe() - self._live_pipe.setZValue(Z_VAL_NODE_WIDGET) - self._live_pipe.activate() - self._live_pipe.style = PIPE_STYLE_DASHED if self._start_port.type == IN_PORT: self._live_pipe.input_port = self._start_port elif self._start_port == OUT_PORT: self._live_pipe.output_port = self._start_port - self.scene().addItem(self._live_pipe) + self._live_pipe.setVisible(True) def end_live_connection(self): """ delete live connection pipe and reset start port. - (removes the pipe item used for drawing the live connection) + (hides the pipe item used for drawing the live connection) """ - if self._live_pipe: - self._live_pipe.delete() - self._live_pipe = None + self._live_pipe.reset_path() + self._live_pipe.setVisible(False) + self._live_pipe.shift_selected = False self._start_port = None def establish_connection(self, start_port, end_port): @@ -596,8 +624,9 @@ def save_dialog(self, current_dir=None, ext=None): def all_pipes(self): pipes = [] + excl = [self._live_pipe, self._pipe_slicer] for item in self.scene().items(): - if isinstance(item, Pipe): + if isinstance(item, Pipe) and item not in excl: pipes.append(item) return pipes From 13f404367b4a61a41cc6934909a37fc26f1c6b50 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Sat, 24 Aug 2019 17:50:41 +1200 Subject: [PATCH 3/6] updated var names --- NodeGraphQt/widgets/viewer.py | 62 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index ff70bbed..8b78ddb0 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -63,13 +63,13 @@ def __init__(self, parent=None): QtWidgets.QRubberBand.Rectangle, self ) - self._live_pipe = LivePipe() - self._live_pipe.setVisible(False) - self.scene().addItem(self._live_pipe) + self._LIVE_PIPE = LivePipe() + self._LIVE_PIPE.setVisible(False) + self.scene().addItem(self._LIVE_PIPE) - self._pipe_slicer = SlicerPipe() - self._pipe_slicer.setVisible(False) - self.scene().addItem(self._pipe_slicer) + self._SLICER_PIPE = SlicerPipe() + self._SLICER_PIPE.setVisible(False) + self.scene().addItem(self._SLICER_PIPE) self._undo_stack = QtWidgets.QUndoStack(self) self._context_menu = QtWidgets.QMenu('main', self) @@ -128,7 +128,7 @@ def _items_near(self, pos, item_type=None, width=20, height=20): x, y = pos.x() - width, pos.y() - height rect = QtCore.QRectF(x, y, width, height) items = [] - excl = [self._live_pipe, self._pipe_slicer] + excl = [self._LIVE_PIPE, self._SLICER_PIPE] for item in self.scene().items(rect): if item in excl: continue @@ -144,7 +144,7 @@ def _on_pipes_sliced(self, path): self.connection_sliced.emit([ [i.input_port, i.output_port] for i in self.scene().items(path) - if isinstance(i, Pipe) and i != self._live_pipe + if isinstance(i, Pipe) and i != self._LIVE_PIPE ]) # --- reimplemented events --- @@ -180,8 +180,8 @@ def mousePressEvent(self, event): # pipe slicer enabled. if self.ALT_state and self.SHIFT_state: - self._pipe_slicer.draw_path(map_pos, map_pos) - self._pipe_slicer.setVisible(True) + self._SLICER_PIPE.draw_path(map_pos, map_pos) + self._SLICER_PIPE.setVisible(True) return # pan mode. @@ -224,11 +224,11 @@ def mouseReleaseEvent(self, event): self.MMB_state = False # hide pipe slicer. - if self._pipe_slicer.isVisible(): - self._on_pipes_sliced(self._pipe_slicer.path()) + if self._SLICER_PIPE.isVisible(): + self._on_pipes_sliced(self._SLICER_PIPE.path()) p = QtCore.QPointF(0.0, 0.0) - self._pipe_slicer.draw_path(p, p) - self._pipe_slicer.setVisible(False) + self._SLICER_PIPE.draw_path(p, p) + self._SLICER_PIPE.setVisible(False) # hide selection marquee if self._rubber_band.isVisible(): @@ -252,11 +252,11 @@ def mouseReleaseEvent(self, event): def mouseMoveEvent(self, event): if self.ALT_state and self.SHIFT_state: - if self.LMB_state and self._pipe_slicer.isVisible(): - p1 = self._pipe_slicer.path().pointAtPercent(0) + if self.LMB_state and self._SLICER_PIPE.isVisible(): + p1 = self._SLICER_PIPE.path().pointAtPercent(0) p2 = self.mapToScene(self._previous_pos) - self._pipe_slicer.draw_path(p1, p2) - self._pipe_slicer.show() + self._SLICER_PIPE.draw_path(p1, p2) + self._SLICER_PIPE.show() self._previous_pos = event.pos() super(NodeViewer, self).mouseMoveEvent(event) return @@ -347,7 +347,7 @@ def sceneMouseMoveEvent(self, event): event (QtWidgets.QGraphicsSceneMouseEvent): The event handler from the QtWidgets.QGraphicsScene """ - if not self._live_pipe.isVisible(): + if not self._LIVE_PIPE.isVisible(): return if not self._start_port: return @@ -361,7 +361,7 @@ def sceneMouseMoveEvent(self, event): pos.setX(pos.x() + x) pos.setY(pos.y() + y) - self._live_pipe.draw_path(self._start_port, cursor_pos=pos) + self._LIVE_PIPE.draw_path(self._start_port, cursor_pos=pos) def sceneMousePressEvent(self, event): """ @@ -417,10 +417,10 @@ def sceneMousePressEvent(self, event): attr = {IN_PORT: 'output_port', OUT_PORT: 'input_port'} self._detached_port = getattr(pipe, attr[from_port.port_type]) self.start_live_connection(from_port) - self._live_pipe.draw_path(self._start_port, cursor_pos=pos) + self._LIVE_PIPE.draw_path(self._start_port, cursor_pos=pos) if self.SHIFT_state: - self._live_pipe.shift_selected = True + self._LIVE_PIPE.shift_selected = True return pipe.delete() @@ -434,7 +434,7 @@ def sceneMouseReleaseEvent(self, event): event (QtWidgets.QGraphicsSceneMouseEvent): The event handler from the QtWidgets.QGraphicsScene """ - if not self._live_pipe.isVisible(): + if not self._LIVE_PIPE.isVisible(): return self._start_port._hovered = False @@ -451,7 +451,7 @@ def sceneMouseReleaseEvent(self, event): # if port disconnected from existing pipe. if end_port is None: - if self._detached_port and not self._live_pipe.shift_selected: + if self._detached_port and not self._LIVE_PIPE.shift_selected: disconnected.append((self._start_port, self._detached_port)) self.connection_changed.emit(disconnected, connected) @@ -515,19 +515,19 @@ def start_live_connection(self, selected_port): return self._start_port = selected_port if self._start_port.type == IN_PORT: - self._live_pipe.input_port = self._start_port + self._LIVE_PIPE.input_port = self._start_port elif self._start_port == OUT_PORT: - self._live_pipe.output_port = self._start_port - self._live_pipe.setVisible(True) + self._LIVE_PIPE.output_port = self._start_port + self._LIVE_PIPE.setVisible(True) def end_live_connection(self): """ delete live connection pipe and reset start port. (hides the pipe item used for drawing the live connection) """ - self._live_pipe.reset_path() - self._live_pipe.setVisible(False) - self._live_pipe.shift_selected = False + self._LIVE_PIPE.reset_path() + self._LIVE_PIPE.setVisible(False) + self._LIVE_PIPE.shift_selected = False self._start_port = None def establish_connection(self, start_port, end_port): @@ -624,7 +624,7 @@ def save_dialog(self, current_dir=None, ext=None): def all_pipes(self): pipes = [] - excl = [self._live_pipe, self._pipe_slicer] + excl = [self._LIVE_PIPE, self._SLICER_PIPE] for item in self.scene().items(): if isinstance(item, Pipe) and item not in excl: pipes.append(item) From bdeb335a2ef89ac910e9ab5da9d42472f9a0c25b Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Sat, 24 Aug 2019 18:05:33 +1200 Subject: [PATCH 4/6] updated draw logic in LivePipe --- NodeGraphQt/qgraphics/pipe.py | 73 ++++++++++++++++++++++++++++++++--- NodeGraphQt/widgets/viewer.py | 2 +- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/NodeGraphQt/qgraphics/pipe.py b/NodeGraphQt/qgraphics/pipe.py index a032f5a1..379478f0 100644 --- a/NodeGraphQt/qgraphics/pipe.py +++ b/NodeGraphQt/qgraphics/pipe.py @@ -301,9 +301,72 @@ def delete(self): class LivePipe(Pipe): - def __init__(self, input_port=None, output_port=None): - super(LivePipe, self).__init__(input_port, output_port) + def __init__(self): + super(LivePipe, self).__init__() self.setZValue(Z_VAL_NODE_WIDGET + 1) - self.activate() - self.style = PIPE_STYLE_DASHED - self.shift_selected = False + + def paint(self, painter, option, widget): + """ + Draws the connection line. + + 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. + """ + color = QtGui.QColor(*PIPE_ACTIVE_COLOR) + pen_style = PIPE_STYLES.get(PIPE_STYLE_DASHED) + pen_width = PIPE_WIDTH + 0.35 + + pen = QtGui.QPen(color, pen_width) + pen.setStyle(pen_style) + pen.setCapStyle(QtCore.Qt.RoundCap) + + painter.save() + painter.setPen(pen) + painter.setRenderHint(painter.Antialiasing, True) + painter.drawPath(self.path()) + + cen_x = self.path().pointAtPercent(0.5).x() + cen_y = self.path().pointAtPercent(0.5).y() + loc_pt = self.path().pointAtPercent(0.9) + tgt_pt = self.path().pointAtPercent(1.0) + + dist = math.hypot(tgt_pt.x() - cen_x, tgt_pt.y() - cen_y) + if dist < 0.05: + painter.restore() + return + + # draw circle + size = 10 * (dist / 100) + if size > 10.0: + size = 10.0 + elif size < 5.0: + size = 5.0 + rect = QtCore.QRectF(cen_x-(size/2), cen_y-(size/2), size, size) + painter.setBrush(color) + painter.setPen(QtGui.QPen(color.darker(130), pen_width)) + painter.drawEllipse(rect) + + # draw arrow + color.setAlpha(255) + painter.setBrush(color.darker(200)) + + pen_width = 0.6 + if dist < 1.0: + pen_width *= (1.0 + dist) + painter.setPen(QtGui.QPen(color, pen_width)) + + transform = QtGui.QTransform() + transform.translate(tgt_pt.x(), tgt_pt.y()) + + radians = math.atan2(tgt_pt.y() - loc_pt.y(), + tgt_pt.x() - loc_pt.x()) + degrees = math.degrees(radians) + 90 + transform.rotate(degrees) + + if dist < 10.0: + transform.scale(0.5, 0.5) + painter.drawPolygon(transform.map(self._arrow)) + painter.restore() diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index 8b78ddb0..6ab43a01 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -5,7 +5,7 @@ from NodeGraphQt import QtGui, QtCore, QtWidgets from NodeGraphQt.constants import (IN_PORT, OUT_PORT, PIPE_LAYOUT_CURVED, - SCENE_AREA,) + SCENE_AREA) from NodeGraphQt.qgraphics.node_abstract import AbstractNodeItem from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem from NodeGraphQt.qgraphics.pipe import Pipe, LivePipe From 1e8c50c0650bac8f61f40686f43fa530c67ae193 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Sat, 24 Aug 2019 23:46:35 +1200 Subject: [PATCH 5/6] live pipe tweaks --- NodeGraphQt/qgraphics/pipe.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/NodeGraphQt/qgraphics/pipe.py b/NodeGraphQt/qgraphics/pipe.py index 379478f0..4ae8e26f 100644 --- a/NodeGraphQt/qgraphics/pipe.py +++ b/NodeGraphQt/qgraphics/pipe.py @@ -339,11 +339,9 @@ def paint(self, painter, option, widget): return # draw circle - size = 10 * (dist / 100) - if size > 10.0: - size = 10.0 - elif size < 5.0: - size = 5.0 + size = 10.0 + if dist < 50.0: + size *= (dist / 50.0) rect = QtCore.QRectF(cen_x-(size/2), cen_y-(size/2), size, size) painter.setBrush(color) painter.setPen(QtGui.QPen(color.darker(130), pen_width)) @@ -355,7 +353,7 @@ def paint(self, painter, option, widget): pen_width = 0.6 if dist < 1.0: - pen_width *= (1.0 + dist) + pen_width *= 1.0 + dist painter.setPen(QtGui.QPen(color, pen_width)) transform = QtGui.QTransform() @@ -366,7 +364,9 @@ def paint(self, painter, option, widget): degrees = math.degrees(radians) + 90 transform.rotate(degrees) - if dist < 10.0: - transform.scale(0.5, 0.5) + scale = 1.0 + if dist < 20.0: + scale = dist / 20.0 + transform.scale(scale, scale) painter.drawPolygon(transform.map(self._arrow)) painter.restore() From 8657d4e805c6d6db11bd01895239fa5ea3ce57c9 Mon Sep 17 00:00:00 2001 From: jchanvfx Date: Mon, 26 Aug 2019 22:14:51 +1200 Subject: [PATCH 6/6] added cursor pos threshold. --- NodeGraphQt/qgraphics/pipe.py | 1 + NodeGraphQt/qgraphics/port.py | 8 ++++++++ NodeGraphQt/widgets/viewer.py | 16 ++++++++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/NodeGraphQt/qgraphics/pipe.py b/NodeGraphQt/qgraphics/pipe.py index 4ae8e26f..7d90bc80 100644 --- a/NodeGraphQt/qgraphics/pipe.py +++ b/NodeGraphQt/qgraphics/pipe.py @@ -304,6 +304,7 @@ class LivePipe(Pipe): def __init__(self): super(LivePipe, self).__init__() self.setZValue(Z_VAL_NODE_WIDGET + 1) + self.shift_selected = False def paint(self, painter, option, widget): """ diff --git a/NodeGraphQt/qgraphics/port.py b/NodeGraphQt/qgraphics/port.py index 4d01431f..3d720ed8 100644 --- a/NodeGraphQt/qgraphics/port.py +++ b/NodeGraphQt/qgraphics/port.py @@ -166,6 +166,14 @@ def connected_ports(self): ports.append(getattr(pipe, port_types[self.port_type])) return ports + @property + def hovered(self): + return self._hovered + + @hovered.setter + def hovered(self, value=False): + self._hovered = value + @property def node(self): return self.parentItem() diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index 6ab43a01..ab89e102 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -1,6 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- import os +import math from NodeGraphQt import QtGui, QtCore, QtWidgets from NodeGraphQt.constants import (IN_PORT, OUT_PORT, @@ -412,7 +413,7 @@ def sceneMousePressEvent(self, event): return pipe = pipe_items[0] from_port = pipe.port_from_pos(pos, True) - from_port._hovered = True + from_port.hovered = True attr = {IN_PORT: 'output_port', OUT_PORT: 'input_port'} self._detached_port = getattr(pipe, attr[from_port.port_type]) @@ -437,7 +438,7 @@ def sceneMouseReleaseEvent(self, event): if not self._LIVE_PIPE.isVisible(): return - self._start_port._hovered = False + self._start_port.hovered = False # find the end port. end_port = None @@ -452,8 +453,15 @@ def sceneMouseReleaseEvent(self, event): # if port disconnected from existing pipe. if end_port is None: if self._detached_port and not self._LIVE_PIPE.shift_selected: - disconnected.append((self._start_port, self._detached_port)) - self.connection_changed.emit(disconnected, connected) + dist = math.hypot(self._previous_pos.x() - self._origin_pos.x(), + self._previous_pos.y() - self._origin_pos.y()) + if dist <= 2.0: # cursor pos threshold. + self.establish_connection(self._start_port, + self._detached_port) + self._detached_port = None + else: + disconnected.append((self._start_port, self._detached_port)) + self.connection_changed.emit(disconnected, connected) self._detached_port = None self.end_live_connection()