Skip to content

Commit bd2b515

Browse files
author
Leandro Inocencio
committed
Merge branch 'master' of https://github.com/jchanvfx/NodeGraphQt
2 parents 4307383 + f113149 commit bd2b515

35 files changed

+2386
-204
lines changed

NodeGraphQt/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,10 @@ def __init__(self):
7474
"""
7575

7676
try:
77-
from Qt import QtWidgets, QtGui, QtCore, QtCompat
77+
from Qt import QtWidgets, QtGui, QtCore, QtCompat, QtOpenGL
7878
except ImportError as ie:
7979
from .vendor.Qt import __version__ as qtpy_ver
80-
from .vendor.Qt import QtWidgets, QtGui, QtCore, QtCompat
80+
from .vendor.Qt import QtWidgets, QtGui, QtCore, QtCompat ,QtOpenGL
8181
print('Cannot import "Qt.py" module falling back on '
8282
'"NodeGraphQt.vendor.Qt ({})"'.format(qtpy_ver))
8383

NodeGraphQt/base/commands.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,29 @@ def set_node_prop(self, name, value):
5151
setattr(view, name, value)
5252

5353
def undo(self):
54-
if self.old_val != self.new_val:
54+
do_undo = False
55+
try:
56+
if self.old_val != self.new_val:
57+
do_undo = True
58+
except:
59+
do_undo = True
60+
61+
if do_undo:
5562
self.set_node_prop(self.name, self.old_val)
5663

5764
# emit property changed signal.
5865
graph = self.node.graph
5966
graph.property_changed.emit(self.node, self.name, self.old_val)
6067

6168
def redo(self):
62-
if self.old_val != self.new_val:
69+
do_redo = False
70+
try:
71+
if self.old_val != self.new_val:
72+
do_redo = True
73+
except:
74+
do_redo = True
75+
76+
if do_redo:
6377
self.set_node_prop(self.name, self.new_val)
6478

6579
# emit property changed signal.

NodeGraphQt/base/graph.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
import os
55
import re
6+
import copy
67

78
from NodeGraphQt import QtCore, QtWidgets
89
from NodeGraphQt.base.commands import (NodeAddedCmd,
@@ -12,7 +13,7 @@
1213
from NodeGraphQt.base.factory import NodeFactory
1314
from NodeGraphQt.base.menu import NodeGraphMenu, NodesMenu
1415
from NodeGraphQt.base.model import NodeGraphModel
15-
from NodeGraphQt.base.node import NodeObject
16+
from NodeGraphQt.base.node import NodeObject, BaseNode
1617
from NodeGraphQt.base.port import Port
1718
from NodeGraphQt.constants import (DRAG_DROP_ID,
1819
PIPE_LAYOUT_CURVED,
@@ -88,7 +89,13 @@ class NodeGraph(QtCore.QObject):
8889
:parameters: :class:`PySide2.QtCore.QMimeData`, :class:`PySide2.QtCore.QPoint`
8990
:emits: mime data, node graph position
9091
"""
92+
session_changed = QtCore.Signal(str)
93+
"""
94+
Signal is triggered when session has been changed.
9195
96+
:parameters: :str
97+
:emits: new session path
98+
"""
9299
def __init__(self, parent=None):
93100
super(NodeGraph, self).__init__(parent)
94101
self.setObjectName('NodeGraphQt')
@@ -132,6 +139,10 @@ def _insert_node(self, pipe, node_id, prev_node_pos):
132139
"""
133140
node = self.get_node_by_id(node_id)
134141

142+
# exclude the BackdropNode
143+
if not isinstance(node, BaseNode):
144+
return
145+
135146
disconnected = [(pipe.input_port, pipe.output_port)]
136147
connected = []
137148

@@ -140,7 +151,9 @@ def _insert_node(self, pipe, node_id, prev_node_pos):
140151
(pipe.output_port, list(node.inputs().values())[0].view)
141152
)
142153
if node.outputs():
143-
connected.append((node.output(0).view, pipe.input_port))
154+
connected.append(
155+
(list(node.outputs().values())[0].view, pipe.input_port)
156+
)
144157

145158
self._undo_stack.beginMacro('inserted node')
146159
self._on_connection_changed(disconnected, connected)
@@ -167,8 +180,13 @@ def _on_property_bin_changed(self, node_id, prop_name, prop_value):
167180
node = self.get_node_by_id(node_id)
168181

169182
# prevent signals from causing a infinite loop.
183+
_exc = [float, int , str, bool, None]
170184
if node.get_property(prop_name) != prop_value:
171-
node.set_property(prop_name, prop_value)
185+
if type(node.get_property(prop_name)) in _exc:
186+
value = prop_value
187+
else:
188+
value = copy.deepcopy(prop_value)
189+
node.set_property(prop_name, value)
172190

173191
def _on_node_double_clicked(self, node_id):
174192
"""
@@ -884,6 +902,7 @@ def clear_session(self):
884902
self._undo_stack.push(NodeRemovedCmd(self, n))
885903
self._undo_stack.clear()
886904
self._model.session = None
905+
self.session_changed.emit("")
887906

888907
def _serialize(self, nodes):
889908
"""
@@ -1022,6 +1041,9 @@ def save_session(self, file_path):
10221041
with open(file_path, 'w') as file_out:
10231042
json.dump(serliazed_data, file_out, indent=2, separators=(',', ':'))
10241043

1044+
self._model.session = file_path
1045+
self.session_changed.emit(file_path)
1046+
10251047
def load_session(self, file_path):
10261048
"""
10271049
Load node graph session layout file.
@@ -1048,6 +1070,7 @@ def load_session(self, file_path):
10481070
self._deserialize(layout_data)
10491071
self._undo_stack.clear()
10501072
self._model.session = file_path
1073+
self.session_changed.emit(file_path)
10511074

10521075
def copy_nodes(self, nodes=None):
10531076
"""
@@ -1198,3 +1221,9 @@ def save_dialog(self, current_dir=None, ext=None):
11981221
str: selected file path.
11991222
"""
12001223
return self._viewer.save_dialog(current_dir, ext)
1224+
1225+
def use_opengl(self):
1226+
"""
1227+
use opengl to draw the graph
1228+
"""
1229+
self._viewer.use_opengl()

NodeGraphQt/base/menu.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,24 +161,32 @@ class NodesMenu(NodeGraphMenu):
161161
nodes_menu = node_graph.get_context_menu('nodes')
162162
"""
163163

164-
def add_command(self, name, func=None, node_type=None):
164+
def add_command(self, name, func=None, node_type=None, node_class=None):
165165
"""
166166
Re-implemented to add a command to the specified node type menu.
167167
168168
Args:
169169
name (str): command name.
170170
func (function): command function eg. "func(``graph``, ``node``)".
171171
node_type (str): specified node type for the command.
172+
node_class (class): specified node class for the command.
172173
173174
Returns:
174175
NodeGraphQt.NodeGraphCommand: the appended command.
175176
"""
176-
if not node_type:
177-
raise NodeMenuError('Node type not specified!')
177+
if not node_type and not node_class:
178+
raise NodeMenuError('Node type or Node class not specified!')
179+
if node_class:
180+
node_type = node_class.__name__
178181

179182
node_menu = self.qmenu.get_menu(node_type)
180183
if not node_menu:
181184
node_menu = BaseMenu(node_type, self.qmenu)
185+
186+
if node_class:
187+
node_menu.node_class = node_class
188+
node_menu.graph = self._graph
189+
182190
self.qmenu.addMenu(node_menu)
183191

184192
if not self.qmenu.isEnabled():
@@ -190,6 +198,14 @@ def add_command(self, name, func=None, node_type=None):
190198
action.setShortcutVisibleInContextMenu(True)
191199
if func:
192200
action.executed.connect(func)
201+
202+
if node_class:
203+
node_menus = self.qmenu.get_menus(node_class)
204+
if node_menu in node_menus:
205+
node_menus.remove(node_menu)
206+
for menu in node_menus:
207+
menu.addAction(action)
208+
193209
qaction = node_menu.addAction(action)
194210
return NodeGraphCommand(self._graph, qaction)
195211

NodeGraphQt/base/model.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,19 @@ def to_dict(self):
232232
node_dict['outputs'] = outputs
233233

234234
custom_props = node_dict.pop('_custom_prop', {})
235+
235236
if custom_props:
237+
# exclude the data which can not be serialized (like numpy array)
238+
to_remove = []
239+
types = [float, str, int, list, dict, bool, None, complex, tuple]
240+
for k, v in custom_props.items():
241+
if type(v) not in types:
242+
try:
243+
json.dumps(v)
244+
except:
245+
to_remove.append(k)
246+
[custom_props.pop(k) for k in to_remove]
247+
236248
node_dict['custom'] = custom_props
237249

238250
exclude = ['_graph_model',

NodeGraphQt/base/node.py

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@
77
NODE_PROP_QTEXTEDIT,
88
NODE_PROP_QCOMBO,
99
NODE_PROP_QCHECKBOX,
10+
NODE_PROP_FILE,
11+
NODE_PROP_FLOAT,
12+
NODE_PROP_INT,
1013
IN_PORT, OUT_PORT)
1114
from NodeGraphQt.errors import PortRegistrationError
1215
from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem
1316
from NodeGraphQt.qgraphics.node_base import NodeItem
1417
from NodeGraphQt.widgets.node_widgets import (NodeComboBox,
1518
NodeLineEdit,
1619
NodeFloatEdit,
17-
NodeCheckBox)
20+
NodeIntEdit,
21+
NodeCheckBox,
22+
NodeFilePath)
1823

1924

2025
class classproperty(object):
@@ -293,8 +298,11 @@ def set_property(self, name, value):
293298
"""
294299

295300
# prevent signals from causing a infinite loop.
296-
if self.get_property(name) == value:
297-
return
301+
try:
302+
if self.get_property(name) == value:
303+
return
304+
except:
305+
pass
298306

299307
if self.graph and name == 'name':
300308
value = self.graph.get_unique_name(value)
@@ -507,7 +515,7 @@ def add_combo_menu(self, name, label='', items=None, tab=None):
507515
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
508516
self.view.add_widget(widget)
509517

510-
def add_text_input(self, name, label='', text='', tab=None):
518+
def add_text_input(self, name, label='', text='', tab=None, multi_line=False):
511519
"""
512520
Creates a custom property with the :meth:`NodeObject.create_property`
513521
function and embeds a :class:`PySide2.QtWidgets.QLineEdit` widget
@@ -522,14 +530,40 @@ def add_text_input(self, name, label='', text='', tab=None):
522530
label (str): label to be displayed.
523531
text (str): pre filled text.
524532
tab (str): name of the widget tab to display in.
533+
multi_line (bool): if create multi line property.
525534
"""
535+
wid_type = NODE_PROP_QTEXTEDIT if multi_line else NODE_PROP_QLINEEDIT
536+
526537
self.create_property(
527-
name, text, widget_type=NODE_PROP_QLINEEDIT, tab=tab)
538+
name, text, widget_type=wid_type, tab=tab)
528539
widget = NodeLineEdit(self.view, name, label, text)
529540
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
530541
self.view.add_widget(widget)
531542

532-
def add_float_input(self, name, label='', value=0.0, tab=None):
543+
def add_file_input(self, name, label='', text='', tab=None, ext="*"):
544+
"""
545+
Creates a custom property with the :meth:`NodeObject.create_property`
546+
function and embeds a :class:`PySide2.QtWidgets.QLineEdit` widget
547+
into the node.
548+
549+
Note:
550+
The embedded widget is wired up to the :meth:`NodeObject.set_property`
551+
function use this function to to update the widget.
552+
553+
Args:
554+
name (str): name for the custom property.
555+
label (str): label to be displayed.
556+
text (str): pre filled text.
557+
tab (str): name of the widget tab to display in.
558+
ext (str): file ext
559+
"""
560+
self.create_property(
561+
name, text, widget_type=NODE_PROP_FILE, tab=tab)
562+
widget = NodeFilePath(self.view, name, label, text,ext)
563+
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
564+
self.view.add_widget(widget)
565+
566+
def add_float_input(self, name, label='', value=0.0, range=None, tab=None):
533567
"""
534568
Creates a custom property with the :meth:`NodeObject.create_property`
535569
function and embeds a :class:`PySide2.QtWidgets.QLineEdit` widget
@@ -543,14 +577,38 @@ def add_float_input(self, name, label='', value=0.0, tab=None):
543577
name (str): name for the custom property.
544578
label (str): label to be displayed.
545579
value (float): pre filled value.
580+
range (tuple): slider range
546581
tab (str): name of the widget tab to display in.
547582
"""
548583
self.create_property(
549-
name, value, widget_type=NODE_PROP_QLINEEDIT, tab=tab)
584+
name, value, widget_type=NODE_PROP_FLOAT, range=range, tab=tab)
550585
widget = NodeFloatEdit(self.view, name, label, value)
551586
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
552587
self.view.add_widget(widget)
553588

589+
def add_int_input(self, name, label='', value=0, range=None, tab=None):
590+
"""
591+
Creates a custom property with the :meth:`NodeObject.create_property`
592+
function and embeds a :class:`PySide2.QtWidgets.QLineEdit` widget
593+
into the node.
594+
595+
Note:
596+
The embedded widget is wired up to the :meth:`NodeObject.set_property`
597+
function use this function to to update the widget.
598+
599+
Args:
600+
name (str): name for the custom property.
601+
label (str): label to be displayed.
602+
value (int): pre filled value.
603+
range (tuple): slider range
604+
tab (str): name of the widget tab to display in.
605+
"""
606+
self.create_property(
607+
name, value, widget_type=NODE_PROP_INT, range=range, tab=tab)
608+
widget = NodeIntEdit(self.view, name, label, value)
609+
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
610+
self.view.add_widget(widget)
611+
554612
def add_checkbox(self, name, label='', text='', state=False, tab=None):
555613
"""
556614
Creates a custom property with the :meth:`NodeObject.create_property`

NodeGraphQt/base/port.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,19 @@ def disconnect_from(self, port=None):
204204
# emit "port_disconnected" signal from the parent graph.
205205
ports = {p.type_(): p for p in [self, port]}
206206
graph.port_disconnected.emit(ports[IN_PORT], ports[OUT_PORT])
207+
208+
@property
209+
def color(self):
210+
return self.__view.color
211+
212+
@color.setter
213+
def color(self, color=(0, 0, 0, 255)):
214+
self.__view.color = color
215+
216+
@property
217+
def border_color(self):
218+
return self.__view.border_color
219+
220+
@border_color.setter
221+
def border_color(self, color=(0, 0, 0, 255)):
222+
self.__view.border_color = color

NodeGraphQt/base/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def setup_context_menu(graph):
3535
file_menu.add_command('Open...', _open_session, QtGui.QKeySequence.Open)
3636
file_menu.add_command('Save...', _save_session, QtGui.QKeySequence.Save)
3737
file_menu.add_command('Save As...', _save_session_as, 'Ctrl+Shift+s')
38-
file_menu.add_command('Clear', _clear_session)
38+
file_menu.add_command('New Session', _new_session)
3939

4040
file_menu.add_separator()
4141

@@ -150,9 +150,9 @@ def _save_session_as(graph):
150150
graph.save_session(file_path)
151151

152152

153-
def _clear_session(graph):
153+
def _new_session(graph):
154154
"""
155-
Prompts a warning dialog to clear the node graph session.
155+
Prompts a warning dialog to new a node graph session.
156156
157157
Args:
158158
graph (NodeGraphQt.NodeGraph): node graph.

0 commit comments

Comments
 (0)