Skip to content

Commit dea4703

Browse files
committed
Merge branch 'master' of https://github.com/jchanvfx/NodeGraphQt into context_menu_types_#127
2 parents c4d9c79 + 6b108c2 commit dea4703

28 files changed

+486
-247
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# sphinx documentation
22
docs/_build/
3+
api
34

45
# python stuff
56
*.pyc
@@ -11,5 +12,9 @@ docs/_build/
1112

1213
# test stuff
1314
dist
15+
build
16+
17+
tests/*
18+
1419
*.egg-info
1520
*.json

NodeGraphQt/base/commands.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,60 @@ def redo(self):
163163
self.node.view.delete()
164164

165165

166+
class NodeInputConnectedCmd(QtWidgets.QUndoCommand):
167+
"""
168+
"BaseNode.on_input_connected()" command.
169+
170+
Args:
171+
src_port (NodeGraphQt.Port): source port.
172+
trg_port (NodeGraphQt.Port): target port.
173+
"""
174+
175+
def __init__(self, src_port, trg_port):
176+
QtWidgets.QUndoCommand.__init__(self)
177+
if src_port.type_() == IN_PORT:
178+
self.source = src_port
179+
self.target = trg_port
180+
else:
181+
self.source = trg_port
182+
self.target = src_port
183+
184+
def undo(self):
185+
node = self.source.node()
186+
node.on_input_disconnected(self.source, self.target)
187+
188+
def redo(self):
189+
node = self.source.node()
190+
node.on_input_connected(self.source, self.target)
191+
192+
193+
class NodeInputDisconnectedCmd(QtWidgets.QUndoCommand):
194+
"""
195+
Node "on_input_disconnected()" command.
196+
197+
Args:
198+
src_port (NodeGraphQt.Port): source port.
199+
trg_port (NodeGraphQt.Port): target port.
200+
"""
201+
202+
def __init__(self, src_port, trg_port):
203+
QtWidgets.QUndoCommand.__init__(self)
204+
if src_port.type_() == IN_PORT:
205+
self.source = src_port
206+
self.target = trg_port
207+
else:
208+
self.source = trg_port
209+
self.target = src_port
210+
211+
def undo(self):
212+
node = self.source.node()
213+
node.on_input_connected(self.source, self.target)
214+
215+
def redo(self):
216+
node = self.source.node()
217+
node.on_input_disconnected(self.source, self.target)
218+
219+
166220
class PortConnectedCmd(QtWidgets.QUndoCommand):
167221
"""
168222
Port connected command.

NodeGraphQt/base/graph.py

Lines changed: 114 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,45 @@
1616
from NodeGraphQt.base.port import Port
1717
from NodeGraphQt.constants import (DRAG_DROP_ID,
1818
PIPE_LAYOUT_CURVED,
19-
PIPE_LAYOUT_STRAIGHT)
19+
PIPE_LAYOUT_STRAIGHT,
20+
PIPE_LAYOUT_ANGLE,
21+
IN_PORT, OUT_PORT)
2022
from NodeGraphQt.widgets.viewer import NodeViewer
2123

2224

2325
class NodeGraph(QtCore.QObject):
2426
"""
25-
base node graph controller.
27+
The ``NodeGraph`` class is the main controller for managing all nodes.
28+
29+
Inherited from: ``PySide2.QtCore.QObject``
30+
31+
.. image:: _images/graph.png
32+
:width: 60%
2633
"""
2734

28-
#: signal emits the node object when a node is created in the node graph.
35+
#:QtCore.Signal: emits the node object when a node is created in the node graph.
2936
node_created = QtCore.Signal(NodeObject)
30-
#: signal emits a list of node ids from the deleted nodes.
37+
#:QtCore.Signal: emits a ``list[str]`` of node ids from the deleted nodes.
3138
nodes_deleted = QtCore.Signal(list)
32-
#: signal emits the node object when selected in the node graph.
39+
#:QtCore.Signal: emits the node object when selected in the node graph.
3340
node_selected = QtCore.Signal(NodeObject)
34-
#: signal triggered when a node is double clicked and emits the node.
41+
#:QtCore.Signal: triggered when a node is double clicked and emits the node.
3542
node_double_clicked = QtCore.Signal(NodeObject)
36-
#: signal for when a node has been connected emits (source port, target port).
43+
#:QtCore.Signal: for when a node has been connected emits (``input port``, ``output port``).
3744
port_connected = QtCore.Signal(Port, Port)
38-
#: signal for when a node has been disconnected emits (source port, target port).
45+
#:QtCore.Signal: for when a node has been disconnected emits (``input port``, ``output port``).
3946
port_disconnected = QtCore.Signal(Port, Port)
40-
#: signal for when a node property has changed emits (node, property name, property value).
47+
#:QtCore.Signal: for when a node property has changed emits (``node``, ``property name``, ``property value``).
4148
property_changed = QtCore.Signal(NodeObject, str, object)
42-
#: signal for when drop data has been added to the graph.
49+
#:QtCore.Signal: for when drop data has been added to the graph.
4350
data_dropped = QtCore.Signal(QtCore.QMimeData, QtCore.QPoint)
4451

4552
def __init__(self, parent=None):
4653
super(NodeGraph, self).__init__(parent)
4754
self.setObjectName('NodeGraphQt')
55+
self._widget = None
4856
self._model = NodeGraphModel()
49-
self._viewer = NodeViewer(parent)
57+
self._viewer = NodeViewer()
5058
self._node_factory = NodeFactory()
5159
self._undo_stack = QtWidgets.QUndoStack(self)
5260

@@ -176,7 +184,7 @@ def _on_connection_changed(self, disconnected, connected):
176184
return
177185

178186
label = 'connect node(s)' if connected else 'disconnect node(s)'
179-
ptypes = {'in': 'inputs', 'out': 'outputs'}
187+
ptypes = {IN_PORT: 'inputs', OUT_PORT: 'outputs'}
180188

181189
self._undo_stack.beginMacro(label)
182190
for p1_view, p2_view in disconnected:
@@ -203,7 +211,7 @@ def _on_connection_sliced(self, ports):
203211
"""
204212
if not ports:
205213
return
206-
ptypes = {'in': 'inputs', 'out': 'outputs'}
214+
ptypes = {IN_PORT: 'inputs', OUT_PORT: 'outputs'}
207215
self._undo_stack.beginMacro('slice connections')
208216
for p1_view, p2_view in ports:
209217
node1 = self._model.nodes[p1_view.node.id]
@@ -216,33 +224,56 @@ def _on_connection_sliced(self, ports):
216224
@property
217225
def model(self):
218226
"""
219-
Returns the model used to store the node graph data.
227+
The model used for storing the node graph data.
220228
221229
Returns:
222230
NodeGraphQt.base.model.NodeGraphModel: node graph model.
223231
"""
224232
return self._model
225233

234+
@property
235+
def widget(self):
236+
"""
237+
The node graph widget for adding into a layout.
238+
239+
Returns:
240+
QtWidgets.QWidget: node graph widget.
241+
"""
242+
if self._widget is None:
243+
self._widget = QtWidgets.QWidget()
244+
layout = QtWidgets.QVBoxLayout(self._widget)
245+
layout.setContentsMargins(0, 0, 0, 0)
246+
layout.addWidget(self._viewer)
247+
return self._widget
248+
226249
def show(self):
227250
"""
228-
Show node graph viewer widget this is just a convenience
229-
function to :meth:`NodeGraph.viewer().show()`.
251+
Show node graph widget this is just a convenience
252+
function to :meth:`NodeGraph.widget().show()`.
230253
"""
231-
self._viewer.show()
254+
self._widget.show()
232255

233256
def close(self):
234257
"""
235258
Close node graph NodeViewer widget this is just a convenience
236-
function to :meth:`NodeGraph.viewer().close()`.
259+
function to :meth:`NodeGraph.widget().close()`.
237260
"""
238-
self._viewer.close()
261+
self._widget.close()
239262

240263
def viewer(self):
241264
"""
242-
Return the node graph viewer widget.
265+
Returns the view interface used by the node graph.
266+
267+
Warnings:
268+
Methods in the `NodeViewer` are used internally
269+
by `NodeGraphQt` components.
270+
271+
See Also:
272+
:attr:`NodeGraph.widget` for adding the node graph into a
273+
QtWidgets.QLayout.
243274
244275
Returns:
245-
NodeGraphQt.widgets.viewer.NodeViewer: viewer widget.
276+
NodeGraphQt.widgets.viewer.NodeViewer: viewer interface.
246277
"""
247278
return self._viewer
248279

@@ -315,7 +346,11 @@ def add_properties_bin(self, prop_bin):
315346

316347
def undo_stack(self):
317348
"""
318-
Returns the undo stack used in the node graph
349+
Returns the undo stack used in the node graph.
350+
351+
See Also:
352+
:meth:`NodeGraph.begin_undo()`,
353+
:meth:`NodeGraph.end_undo()`
319354
320355
Returns:
321356
QtWidgets.QUndoStack: undo stack.
@@ -325,7 +360,15 @@ def undo_stack(self):
325360
def clear_undo_stack(self):
326361
"""
327362
Clears the undo stack.
328-
(convenience function to :meth:`NodeGraph.undo_stack().clear`)
363+
364+
Note:
365+
Convenience function to
366+
:meth:`NodeGraph.undo_stack().clear()`
367+
368+
See Also:
369+
:meth:`NodeGraph.begin_undo()`,
370+
:meth:`NodeGraph.end_undo()`,
371+
:meth:`NodeGraph.undo_stack()`
329372
"""
330373
self._undo_stack.clear()
331374

@@ -411,19 +454,26 @@ def set_acyclic(self, mode=False):
411454
self._model.acyclic = mode
412455
self._viewer.acyclic = mode
413456

414-
def set_pipe_style(self, style=None):
457+
def set_pipe_style(self, style=PIPE_LAYOUT_CURVED):
415458
"""
416-
Set node graph pipes to be drawn straight or curved by default
417-
all pipes are set curved. (default=0)
459+
Set node graph pipes to be drawn as straight, curved or angled.
460+
461+
Note:
462+
By default all pipes are set curved.
463+
464+
Pipe Layout Styles:
418465
419-
``NodeGraphQt.constants.PIPE_LAYOUT_CURVED`` = 0
420-
``NodeGraphQt.constants.PIPE_LAYOUT_STRAIGHT`` = 1
466+
* :attr:`NodeGraphQt.constants.PIPE_LAYOUT_CURVED`
467+
* :attr:`NodeGraphQt.constants.PIPE_LAYOUT_STRAIGHT`
468+
* :attr:`NodeGraphQt.constants.PIPE_LAYOUT_ANGLE`
421469
422470
Args:
423-
style (int): pipe style.
471+
style (int): pipe layout style.
424472
"""
425-
pipe_default = max([PIPE_LAYOUT_CURVED, PIPE_LAYOUT_STRAIGHT])
426-
style = PIPE_LAYOUT_STRAIGHT if style > pipe_default else style
473+
pipe_max = max([PIPE_LAYOUT_CURVED,
474+
PIPE_LAYOUT_STRAIGHT,
475+
PIPE_LAYOUT_ANGLE])
476+
style = style if 0 >= style >= pipe_max else PIPE_LAYOUT_CURVED
427477
self._viewer.set_pipe_layout(style)
428478

429479
def fit_to_selection(self):
@@ -480,7 +530,8 @@ def registered_nodes(self):
480530
"""
481531
Return a list of all node types that have been registered.
482532
483-
To register a node see :meth:`NodeGraph.register_node`
533+
See Also:
534+
To register a node :meth:`NodeGraph.register_node`
484535
485536
Returns:
486537
list[str]: list of node type identifiers.
@@ -502,7 +553,8 @@ def create_node(self, node_type, name=None, selected=True, color=None,
502553
"""
503554
Create a new node in the node graph.
504555
505-
(To list all node types see :meth:`NodeGraph.registered_nodes`)
556+
See Also:
557+
To list all node types :meth:`NodeGraph.registered_nodes`
506558
507559
Args:
508560
node_type (str): node instance type.
@@ -753,14 +805,16 @@ def _serialize(self, nodes):
753805
for pname, conn_data in inputs.items():
754806
for conn_id, prt_names in conn_data.items():
755807
for conn_prt in prt_names:
756-
pipe = {'in': [n_id, pname], 'out': [conn_id, conn_prt]}
808+
pipe = {IN_PORT: [n_id, pname],
809+
OUT_PORT: [conn_id, conn_prt]}
757810
if pipe not in serial_data['connections']:
758811
serial_data['connections'].append(pipe)
759812

760813
for pname, conn_data in outputs.items():
761814
for conn_id, prt_names in conn_data.items():
762815
for conn_prt in prt_names:
763-
pipe = {'out': [n_id, pname], 'in': [conn_id, conn_prt]}
816+
pipe = {OUT_PORT: [n_id, pname],
817+
IN_PORT: [conn_id, conn_prt]}
764818
if pipe not in serial_data['connections']:
765819
serial_data['connections'].append(pipe)
766820

@@ -837,6 +891,17 @@ def serialize_session(self):
837891
"""
838892
return self._serialize(self.all_nodes())
839893

894+
def deserialize_session(self, layout_data):
895+
"""
896+
Load node graph session from a dictionary object.
897+
898+
Args:
899+
layout_data (dict): dictionary object containing a node session.
900+
"""
901+
self.clear_session()
902+
self._deserialize(layout_data)
903+
self._undo_stack.clear()
904+
840905
def save_session(self, file_path):
841906
"""
842907
Saves the current node graph session layout to a `JSON` formatted file.
@@ -940,7 +1005,8 @@ def disable_nodes(self, nodes, mode=None):
9401005
"""
9411006
Set weather to Disable or Enable specified nodes.
9421007
943-
see: :meth:`NodeObject.set_disabled`
1008+
See Also:
1009+
:meth:`NodeObject.set_disabled`
9441010
9451011
Args:
9461012
nodes (list[NodeGraphQt.BaseNode]): list of node instances.
@@ -964,7 +1030,9 @@ def question_dialog(self, text, title='Node Graph'):
9641030
Prompts a question open dialog with "Yes" and "No" buttons in
9651031
the node graph.
9661032
967-
(convenience function to :meth:`NodeGraph.viewer().question_dialog`)
1033+
Note:
1034+
Convenience function to
1035+
:meth:`NodeGraphQt.NodeGraph.viewer().question_dialog`
9681036
9691037
Args:
9701038
text (str): question text.
@@ -979,7 +1047,9 @@ def message_dialog(self, text, title='Node Graph'):
9791047
"""
9801048
Prompts a file open dialog in the node graph.
9811049
982-
(convenience function to :meth:`NodeGraph.viewer().message_dialog`)
1050+
Note:
1051+
Convenience function to
1052+
:meth:`NodeGraphQt.NodeGraph.viewer().message_dialog`
9831053
9841054
Args:
9851055
text (str): message text.
@@ -991,7 +1061,9 @@ def load_dialog(self, current_dir=None, ext=None):
9911061
"""
9921062
Prompts a file open dialog in the node graph.
9931063
994-
(convenience function to :meth:`NodeGraph.viewer().load_dialog`)
1064+
Note:
1065+
Convenience function to
1066+
:meth:`NodeGraphQt.NodeGraph.viewer().load_dialog`
9951067
9961068
Args:
9971069
current_dir (str): path to a directory.
@@ -1006,7 +1078,9 @@ def save_dialog(self, current_dir=None, ext=None):
10061078
"""
10071079
Prompts a file save dialog in the node graph.
10081080
1009-
(convenience function to :meth:`NodeGraph.viewer().save_dialog`)
1081+
Note:
1082+
Convenience function to
1083+
:meth:`NodeGraphQt.NodeGraph.viewer().save_dialog`
10101084
10111085
Args:
10121086
current_dir (str): path to a directory.

0 commit comments

Comments
 (0)