Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions NodeGraphQt/base/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,60 @@ def redo(self):
self.node.view.delete()


class NodeInputConnectedCmd(QtWidgets.QUndoCommand):
"""
"BaseNode.on_input_connected()" command.

Args:
src_port (NodeGraphQt.Port): source port.
trg_port (NodeGraphQt.Port): target port.
"""

def __init__(self, src_port, trg_port):
QtWidgets.QUndoCommand.__init__(self)
if src_port.type_() == IN_PORT:
self.source = src_port
self.target = trg_port
else:
self.source = trg_port
self.target = src_port

def undo(self):
node = self.source.node()
node.on_input_disconnected(self.source, self.target)

def redo(self):
node = self.source.node()
node.on_input_connected(self.source, self.target)


class NodeInputDisconnectedCmd(QtWidgets.QUndoCommand):
"""
Node "on_input_disconnected()" command.

Args:
src_port (NodeGraphQt.Port): source port.
trg_port (NodeGraphQt.Port): target port.
"""

def __init__(self, src_port, trg_port):
QtWidgets.QUndoCommand.__init__(self)
if src_port.type_() == IN_PORT:
self.source = src_port
self.target = trg_port
else:
self.source = trg_port
self.target = src_port

def undo(self):
node = self.source.node()
node.on_input_connected(self.source, self.target)

def redo(self):
node = self.source.node()
node.on_input_disconnected(self.source, self.target)


class PortConnectedCmd(QtWidgets.QUndoCommand):
"""
Port connected command.
Expand Down
36 changes: 20 additions & 16 deletions NodeGraphQt/base/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
from NodeGraphQt.base.port import Port
from NodeGraphQt.constants import (DRAG_DROP_ID,
PIPE_LAYOUT_CURVED,
PIPE_LAYOUT_STRAIGHT, PIPE_LAYOUT_ANGLE)
PIPE_LAYOUT_STRAIGHT,
PIPE_LAYOUT_ANGLE,
IN_PORT, OUT_PORT)
from NodeGraphQt.widgets.viewer import NodeViewer


Expand All @@ -25,21 +27,21 @@ class NodeGraph(QtCore.QObject):
base node graph controller.
"""

#: (signal) emits the node object when a node is created in the node graph.
#:QtCore.Signal: emits the node object when a node is created in the node graph.
node_created = QtCore.Signal(NodeObject)
#: (signal) emits a list of node ids from the deleted nodes.
#:QtCore.Signal: emits a ``list[str]`` of node ids from the deleted nodes.
nodes_deleted = QtCore.Signal(list)
#: (signal) emits the node object when selected in the node graph.
#:QtCore.Signal: emits the node object when selected in the node graph.
node_selected = QtCore.Signal(NodeObject)
#: (signal) triggered when a node is double clicked and emits the node.
#:QtCore.Signal: triggered when a node is double clicked and emits the node.
node_double_clicked = QtCore.Signal(NodeObject)
#: (signal) for when a node has been connected emits (source port, target port).
#:QtCore.Signal: for when a node has been connected emits (``input port``, ``output port``).
port_connected = QtCore.Signal(Port, Port)
#: (signal) for when a node has been disconnected emits (source port, target port).
#:QtCore.Signal: for when a node has been disconnected emits (``input port``, ``output port``).
port_disconnected = QtCore.Signal(Port, Port)
#: (signal) for when a node property has changed emits (node, property name, property value).
#:QtCore.Signal: for when a node property has changed emits (``node``, ``property name``, ``property value``).
property_changed = QtCore.Signal(NodeObject, str, object)
#: (signal) for when drop data has been added to the graph.
#:QtCore.Signal: for when drop data has been added to the graph.
data_dropped = QtCore.Signal(QtCore.QMimeData, QtCore.QPoint)

def __init__(self, parent=None):
Expand Down Expand Up @@ -177,7 +179,7 @@ def _on_connection_changed(self, disconnected, connected):
return

label = 'connect node(s)' if connected else 'disconnect node(s)'
ptypes = {'in': 'inputs', 'out': 'outputs'}
ptypes = {IN_PORT: 'inputs', OUT_PORT: 'outputs'}

self._undo_stack.beginMacro(label)
for p1_view, p2_view in disconnected:
Expand All @@ -204,7 +206,7 @@ def _on_connection_sliced(self, ports):
"""
if not ports:
return
ptypes = {'in': 'inputs', 'out': 'outputs'}
ptypes = {IN_PORT: 'inputs', OUT_PORT: 'outputs'}
self._undo_stack.beginMacro('slice connections')
for p1_view, p2_view in ports:
node1 = self._model.nodes[p1_view.node.id]
Expand Down Expand Up @@ -242,14 +244,14 @@ def widget(self):
def show(self):
"""
Show node graph widget this is just a convenience
function to :meth:`NodeGraphQt.NodeGraph.widget.show()`.
function to :meth:`NodeGraph.widget().show()`.
"""
self._widget.show()

def close(self):
"""
Close node graph NodeViewer widget this is just a convenience
function to :meth:`NodeGraphQt.NodeGraph.widget.close()`.
function to :meth:`NodeGraph.widget().close()`.
"""
self._widget.close()

Expand Down Expand Up @@ -356,7 +358,7 @@ def clear_undo_stack(self):

Note:
Convenience function to
:meth:`NodeGraphQt.NodeGraph.undo_stack().clear`
:meth:`NodeGraph.undo_stack().clear()`

See Also:
:meth:`NodeGraph.begin_undo()`,
Expand Down Expand Up @@ -772,14 +774,16 @@ def _serialize(self, nodes):
for pname, conn_data in inputs.items():
for conn_id, prt_names in conn_data.items():
for conn_prt in prt_names:
pipe = {'in': [n_id, pname], 'out': [conn_id, conn_prt]}
pipe = {IN_PORT: [n_id, pname],
OUT_PORT: [conn_id, conn_prt]}
if pipe not in serial_data['connections']:
serial_data['connections'].append(pipe)

for pname, conn_data in outputs.items():
for conn_id, prt_names in conn_data.items():
for conn_prt in prt_names:
pipe = {'out': [n_id, pname], 'in': [conn_id, conn_prt]}
pipe = {OUT_PORT: [n_id, pname],
IN_PORT: [conn_id, conn_prt]}
if pipe not in serial_data['connections']:
serial_data['connections'].append(pipe)

Expand Down
67 changes: 63 additions & 4 deletions NodeGraphQt/base/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ class NodeObject(object):
qgraphics_item (AbstractNodeItem): graphic item used for drawing.
"""

#: (str) unique node identifier domain.
#:str: unique node identifier domain.
__identifier__ = 'nodeGraphQt.nodes'

#: (str) base node name.
#:str: base node name.
NODE_NAME = None

def __init__(self, qgraphics_item=None):
assert qgraphics_item, 'qgraphics item cannot be None.'
assert qgraphics_item, 'qgraphics_item item cannot be None.'
self._graph = None
self._model = NodeModel()
self._model.type_ = self.type_
Expand Down Expand Up @@ -261,7 +261,7 @@ def get_property(self, name):
object: property data.
"""
if self.graph and name == 'selected':
self.model.set_property(self.view.selected)
self.model.set_property(name, self.view.selected)

return self.model.get_property(name)

Expand Down Expand Up @@ -602,6 +602,65 @@ def set_output(self, index, port):
src_port = self.output(index)
src_port.connect_to(port)

def connected_input_nodes(self):
"""
Returns all nodes connected from the input ports.

Returns:
dict: {<input_port>: <node_list>}
"""
nodes = {}
for p in self.input_ports():
nodes[p] = [cp.node() for cp in p.connected_ports()]
return nodes

def connected_output_nodes(self):
"""
Returns all nodes connected from the output ports.

Returns:
dict: {<output_port>: <node_list>}
"""
nodes = {}
for p in self.output_ports():
nodes[p] = [cp.node() for cp in p.connected_ports()]
return nodes

def on_input_connected(self, in_port, out_port):
"""
Callback triggered when a new pipe connection is made.

*The default of this function does nothing re-implement if you require
logic to run for this event.*

Note:
to work with undo & redo for this method re-implement
:meth:`BaseNode.on_input_disconnected` with the reverse logic.

Args:
in_port (NodeGraphQt.Port): source input port from this node.
out_port (NodeGraphQt.Port): output port that connected to this node.
"""
return

def on_input_disconnected(self, in_port, out_port):
"""
Callback triggered when a pipe connection has been disconnected
from a INPUT port.

*The default of this function does nothing re-implement if you require
logic to run for this event.*

Note:
to work with undo & redo for this method re-implement
:meth:`BaseNode.on_input_connected` with the reverse logic.

Args:
in_port (NodeGraphQt.Port): source input port from this node.
out_port (NodeGraphQt.Port): output port that was disconnected.
"""
return


class BackdropNode(NodeObject):
"""
Expand Down
23 changes: 18 additions & 5 deletions NodeGraphQt/base/port.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/usr/bin/python
from NodeGraphQt.base.commands import (PortConnectedCmd,
PortDisconnectedCmd,
PortVisibleCmd)
PortVisibleCmd,
NodeInputConnectedCmd,
NodeInputDisconnectedCmd)
from NodeGraphQt.base.model import PortModel
from NodeGraphQt.constants import IN_PORT, OUT_PORT

Expand Down Expand Up @@ -66,7 +68,7 @@ def node(self):
Return the parent node.

Returns:
NodeGraphQt.NodeObject: parent node object.
NodeGraphQt.BaseNode: parent node object.
"""
return self.model.node

Expand Down Expand Up @@ -138,8 +140,8 @@ def connect_to(self, port=None):

graph = self.node().graph
viewer = graph.viewer()
undo_stack = graph.undo_stack()

undo_stack = graph.undo_stack()
undo_stack.beginMacro('connect port')

pre_conn_port = None
Expand All @@ -149,26 +151,33 @@ def connect_to(self, port=None):

if not port:
if pre_conn_port:
undo_stack.push(NodeInputDisconnectedCmd(self, port))
undo_stack.push(PortDisconnectedCmd(self, port))
return

if graph.acyclic() and viewer.acyclic_check(self.view, port.view):
if pre_conn_port:
undo_stack.push(NodeInputDisconnectedCmd(self, pre_conn_port))
undo_stack.push(PortDisconnectedCmd(self, pre_conn_port))
return

trg_conn_ports = port.connected_ports()
if not port.multi_connection() and trg_conn_ports:
dettached_port = trg_conn_ports[0]
undo_stack.push(NodeInputDisconnectedCmd(port, dettached_port))
undo_stack.push(PortDisconnectedCmd(port, dettached_port))
if pre_conn_port:
undo_stack.push(NodeInputDisconnectedCmd(self, pre_conn_port))
undo_stack.push(PortDisconnectedCmd(self, pre_conn_port))

undo_stack.push(NodeInputConnectedCmd(self, port))
undo_stack.push(PortConnectedCmd(self, port))

undo_stack.endMacro()

# emit "port_connected" signal from the parent graph.
graph.port_connected.emit(self, port)
ports = {p.type_(): p for p in [self, port]}
graph.port_connected.emit(ports[IN_PORT], ports[OUT_PORT])

def disconnect_from(self, port=None):
"""
Expand All @@ -181,7 +190,11 @@ def disconnect_from(self, port=None):
if not port:
return
graph = self.node().graph
graph.undo_stack().beginMacro('disconnect port')
graph.undo_stack().push(NodeInputDisconnectedCmd(self, port))
graph.undo_stack().push(PortDisconnectedCmd(self, port))
graph.undo_stack().endMacro()

# emit "port_disconnected" signal from the parent graph.
graph.port_disconnected.emit(self, port)
ports = {p.type_(): p for p in [self, port]}
graph.port_disconnected.emit(ports[IN_PORT], ports[OUT_PORT])
6 changes: 3 additions & 3 deletions NodeGraphQt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
# === PIPE ===

PIPE_WIDTH = 1.2
PIPE_STYLE_DEFAULT = 'line'
PIPE_STYLE_DASHED = 'dashed'
PIPE_STYLE_DOTTED = 'dotted'
PIPE_STYLE_DEFAULT = 0
PIPE_STYLE_DASHED = 1
PIPE_STYLE_DOTTED = 2
PIPE_DEFAULT_COLOR = (175, 95, 30, 255)
PIPE_DISABLED_COLOR = (190, 20, 20, 255)
PIPE_ACTIVE_COLOR = (70, 255, 220, 255)
Expand Down
5 changes: 5 additions & 0 deletions docs/_static/ngqt.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ code span.pre {
color: #5cafb9;
}

/* properties */
em.property {
color: #5d86c3;
}

/* tables */
table.docutils td,
table.docutils th {
Expand Down