Skip to content

Commit

Permalink
Merge pull request #116 from jchanvfx/slice_pipes
Browse files Browse the repository at this point in the history
pipe connection slicer tool
  • Loading branch information
jchanvfx authored Jun 19, 2019
2 parents d36393e + 6e9bfef commit 46b7207
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 6 deletions.
21 changes: 21 additions & 0 deletions NodeGraphQt/base/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def __repr__(self):
def _wire_signals(self):
# internal signals.
self._viewer.search_triggered.connect(self._on_search_triggered)
self._viewer.connection_sliced.connect(self._on_connection_sliced)
self._viewer.connection_changed.connect(self._on_connection_changed)
self._viewer.moved_nodes.connect(self._on_nodes_moved)
self._viewer.node_double_clicked.connect(self._on_node_double_clicked)
Expand Down Expand Up @@ -188,6 +189,26 @@ def _on_connection_changed(self, disconnected, connected):
port1.connect_to(port2)
self._undo_stack.endMacro()

def _on_connection_sliced(self, ports):
"""
slot when connection pipes have been sliced.
Args:
ports (list[list[widgets.port.PortItem]]):
pair list of port connections (in port, out port)
"""
if not ports:
return
ptypes = {'in': 'inputs', 'out': 'outputs'}
self._undo_stack.beginMacro('slice connections')
for p1_view, p2_view in ports:
node1 = self._model.nodes[p1_view.node.id]
node2 = self._model.nodes[p2_view.node.id]
port1 = getattr(node1, ptypes[p1_view.port_type])()[p1_view.name]
port2 = getattr(node2, ptypes[p2_view.port_type])()[p2_view.name]
port1.disconnect_from(port2)
self._undo_stack.endMacro()

@property
def model(self):
"""
Expand Down
1 change: 1 addition & 0 deletions NodeGraphQt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
PIPE_DISABLED_COLOR = (190, 20, 20, 255)
PIPE_ACTIVE_COLOR = (70, 255, 220, 255)
PIPE_HIGHLIGHT_COLOR = (232, 184, 13, 255)
PIPE_SLICER_COLOR = (255, 50, 75)
#: The draw the connection pipes as straight lines.
PIPE_LAYOUT_STRAIGHT = 0
#: The draw the connection pipes as curved lines.
Expand Down
63 changes: 63 additions & 0 deletions NodeGraphQt/qgraphics/slicer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/python
from NodeGraphQt import QtCore, QtGui, QtWidgets
from NodeGraphQt.constants import Z_VAL_NODE_WIDGET, PIPE_SLICER_COLOR


class SlicerPipe(QtWidgets.QGraphicsPathItem):
"""
Base item used for drawing the pipe connection slicer.
"""

def __init__(self):
super(SlicerPipe, self).__init__()
self.setZValue(Z_VAL_NODE_WIDGET + 2)

def paint(self, painter, option, widget):
"""
Draws the slicer pipe.
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_SLICER_COLOR)
p1 = self.path().pointAtPercent(0)
p2 = self.path().pointAtPercent(1)
size = 6.0
offset = size / 2

painter.save()
painter.setRenderHint(painter.Antialiasing, True)

font = painter.font()
font.setPointSize(12)
painter.setFont(font)
text = 'slice'
text_x = painter.fontMetrics().width(text) / 2
text_y = painter.fontMetrics().height() / 1.5
text_pos = QtCore.QPointF(p1.x() - text_x, p1.y() - text_y)
text_color = QtGui.QColor(*PIPE_SLICER_COLOR)
text_color.setAlpha(80)
painter.setPen(QtGui.QPen(text_color, 1.5, QtCore.Qt.SolidLine))
painter.drawText(text_pos, text)

painter.setPen(QtGui.QPen(color, 1.5, QtCore.Qt.DashLine))
painter.drawPath(self.path())

painter.setPen(QtGui.QPen(color, 1.5, QtCore.Qt.SolidLine))
painter.setBrush(color)

rect = QtCore.QRectF(p1.x() - offset, p1.y() - offset, size, size)
painter.drawEllipse(rect)

rect = QtCore.QRectF(p2.x() - offset, p2.y() - offset, size, size)
painter.drawEllipse(rect)
painter.restore()

def draw_path(self, p1, p2):
path = QtGui.QPainterPath()
path.moveTo(p1)
path.lineTo(p2)
self.setPath(path)
44 changes: 43 additions & 1 deletion NodeGraphQt/widgets/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem
from NodeGraphQt.qgraphics.pipe import Pipe
from NodeGraphQt.qgraphics.port import PortItem
from NodeGraphQt.qgraphics.slicer import SlicerPipe
from NodeGraphQt.widgets.scene import NodeScene
from NodeGraphQt.widgets.stylesheet import STYLE_QMENU
from NodeGraphQt.widgets.tab_search import TabSearchWidget
Expand All @@ -30,6 +31,7 @@ class NodeViewer(QtWidgets.QGraphicsView):

moved_nodes = QtCore.Signal(dict)
search_triggered = QtCore.Signal(str, tuple)
connection_sliced = QtCore.Signal(list)
connection_changed = QtCore.Signal(list, list)

# pass through signals
Expand Down Expand Up @@ -63,6 +65,10 @@ def __init__(self, parent=None):
self._rubber_band = QtWidgets.QRubberBand(
QtWidgets.QRubberBand.Rectangle, self
)
self._pipe_slicer = SlicerPipe()
self._pipe_slicer.setVisible(False)
self.scene().addItem(self._pipe_slicer)

self._undo_stack = QtWidgets.QUndoStack(self)
self._context_menu = QtWidgets.QMenu('main', self)
self._context_menu.setStyleSheet(STYLE_QMENU)
Expand Down Expand Up @@ -126,6 +132,12 @@ def _on_search_submitted(self, node_type):
pos = self.mapToScene(self._previous_pos)
self.search_triggered.emit(node_type, (pos.x(), pos.y()))

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

# --- reimplemented events ---

def resizeEvent(self, event):
Expand All @@ -138,6 +150,7 @@ def contextMenuEvent(self, 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:
Expand All @@ -152,10 +165,19 @@ def mousePressEvent(self, event):
if self._search_widget.isVisible():
self.tab_search_toggle()

# cursor pos.
map_pos = self.mapToScene(event.pos())

# pipe slicer enabled.
if event.modifiers() == (QtCore.Qt.AltModifier | QtCore.Qt.ShiftModifier):
self._pipe_slicer.draw_path(map_pos, map_pos)
self._pipe_slicer.setVisible(True)
return

if alt_modifier:
return

items = self._items_near(self.mapToScene(event.pos()), None, 20, 20)
items = self._items_near(map_pos, None, 20, 20)
nodes = [i for i in items if isinstance(i, AbstractNodeItem)]

# toggle extend node selection.
Expand Down Expand Up @@ -188,6 +210,13 @@ def mouseReleaseEvent(self, event):
elif event.button() == QtCore.Qt.MiddleButton:
self.MMB_state = False

# hide pipe slicer.
if self._pipe_slicer.isVisible():
self._on_pipes_sliced(self._pipe_slicer.path())
p = QtCore.QPointF(0.0, 0.0)
self._pipe_slicer.draw_path(p, p)
self._pipe_slicer.setVisible(False)

# hide selection marquee
if self._rubber_band.isVisible():
rect = self._rubber_band.rect()
Expand All @@ -211,6 +240,15 @@ def mouseReleaseEvent(self, 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:
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

if self.MMB_state and alt_modifier:
pos_x = (event.x() - self._previous_pos.x())
zoom = 0.1 if pos_x > 0 else -0.1
Expand Down Expand Up @@ -296,6 +334,10 @@ def sceneMousePressEvent(self, event):
event (QtWidgets.QGraphicsScenePressEvent):
The event handler from the QtWidgets.QGraphicsScene
"""
# pipe slicer enabled.
if event.modifiers() == (QtCore.Qt.AltModifier | QtCore.Qt.ShiftModifier):
return
# viewer pan mode.
if event.modifiers() == QtCore.Qt.AltModifier:
return

Expand Down
Binary file added docs/_images/slicer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion docs/_static/ngqt.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ code span.pre {
}

/* tables */
table.docutils td, table.docutils th {
table.docutils td,
table.docutils th {
padding: 4px 8px;
border-top: 0;
border-left: 0;
Expand All @@ -52,6 +53,10 @@ table.docutils td, table.docutils th {
background: #24272b;
}

table.align-center {
margin-left: unset;
}

/*-----------------------------------------*/

/* modules index */
Expand Down
15 changes: 15 additions & 0 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ Navigation
| Pan | *Alt + LMB + Drag* or *MMB + Drag* |
+---------------+----------------------------------------------+

Port Connections
================

.. image:: _images/slicer.png
:width: 600px

Connection pipes can be disconnected easily with the built in slice tool.

+---------------------+----------------------------+
| action | controls |
+=====================+============================+
| Slice connections | *Alt + Shift + LMB + Drag* |
+---------------------+----------------------------+


Node Search
===========

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
PySide2>=5.12
Qt.py>=1.2.0.b2
python>=3.6
python>=3.6
10 changes: 7 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
# -*- coding: utf-8 -*-
import setuptools

from NodeGraphQt import __version__ as version
import NodeGraphQt

with open('README.md', 'r') as fh:
long_description = fh.read()

with open('requirements.txt') as f:
requirements = f.read().splitlines()

description = (
'Node graph framework that can be re-implemented into applications that '
'supports PySide & PySide2'
Expand All @@ -19,7 +22,8 @@

setuptools.setup(
name='NodeGraphQt',
version=version,
version=NodeGraphQt.__version__,
install_requires=requirements,
author='Johnny Chan',
author_email='johnny@chantasticvfx.com',
description=description,
Expand All @@ -28,7 +32,7 @@
url='https://github.com/jchanvfx/NodeGraphQt',
packages=setuptools.find_packages(exclude=["example_nodes"]),
classifiers=classifiers,
include_package_data=True,
include_package_data=True
)


Expand Down

0 comments on commit 46b7207

Please sign in to comment.