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
43 changes: 39 additions & 4 deletions NodeGraphQt/base/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@
from NodeGraphQt.widgets.viewer import NodeViewer


class QWidgetDrops(QtWidgets.QWidget):
def dragEnterEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()

def dragMoveEvent(self, event):
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()

def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
for url in event.mimeData().urls():
self.import_session(url.toLocalFile())
else:
e.ignore()


class NodeGraph(QtCore.QObject):
"""
The ``NodeGraph`` class is the main controller for managing all nodes.
Expand Down Expand Up @@ -110,6 +133,7 @@ def __init__(self, parent=None):
self._viewer.need_show_tab_search.connect(self._toggle_tab_search)

self._wire_signals()
self.widget.setAcceptDrops(True)

def __repr__(self):
return '<{} object at {}>'.format(self.__class__.__name__, hex(id(self)))
Expand Down Expand Up @@ -325,8 +349,10 @@ def widget(self):
PySide2.QtWidgets.QWidget: node graph widget.
"""
if self._widget is None:
self._widget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(self._widget)
self._widget = QWidgetDrops()
self._widget.import_session = self.import_session

layout = QtWidgets.QVBoxLayout(self._widget)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self._viewer)
return self._widget
Expand Down Expand Up @@ -1046,16 +1072,25 @@ def save_session(self, file_path):
def load_session(self, file_path):
"""
Load node graph session layout file.

Args:
file_path (str): path to the serialized layout file.
"""
self.clear_session()
self.import_session(file_path)

def import_session(self, file_path):
"""
Import node graph session layout file.

Args:
file_path (str): path to the serialized layout file.
"""

file_path = file_path.strip()
if not os.path.isfile(file_path):
raise IOError('file does not exist.')

self.clear_session()

try:
with open(file_path) as data_file:
layout_data = json.load(data_file)
Expand Down
15 changes: 15 additions & 0 deletions NodeGraphQt/base/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def setup_context_menu(graph):

# create "File" menu.
file_menu.add_command('Open...', _open_session, QtGui.QKeySequence.Open)
file_menu.add_command('Import...', _import_session, QtGui.QKeySequence.Open)
file_menu.add_command('Save...', _save_session, QtGui.QKeySequence.Save)
file_menu.add_command('Save As...', _save_session_as, 'Ctrl+Shift+s')
file_menu.add_command('New Session', _new_session)
Expand Down Expand Up @@ -119,6 +120,20 @@ def _open_session(graph):
graph.load_session(file_path)


def _import_session(graph):
"""
Prompts a file open dialog to load a session.

Args:
graph (NodeGraphQt.NodeGraph): node graph.
"""
current = graph.current_session()
viewer = graph.viewer()
file_path = viewer.load_dialog(current)
if file_path:
graph.import_session(file_path)


def _save_session(graph):
"""
Prompts a file save dialog to serialize a session if required.
Expand Down
2 changes: 1 addition & 1 deletion example_math_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ def show_nodes_list(node):

# registered nodes.
[graph.register_node(n) for n in Nodes]
graph.load_session('example.nodes')
graph.load_session(r'example_nodes\networks\example.nodes')

app.exec_()
51 changes: 41 additions & 10 deletions example_nodes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,58 @@
#!/usr/bin/python
import os
import sys
import inspect
import importlib
import ast


VALID_NODE_TYPE = ['BaseNode', 'AutoNode']


def detectNodesFromText(filepath):
"""returns Node names from a python script"""
froms = []

with open(filepath, "r") as source:
tree = ast.parse(source.read())

for node in tree.body:
if isinstance(node, ast.ClassDef):
for base in node.bases:
if base.id in VALID_NODE_TYPE:
for indef in node.body:
if isinstance(indef, ast.Assign):
for target in indef.targets:
if target.id == '__identifier__':
froms.append(node.name)
return froms


def getNodesRecursively(path=__file__):
""" Returns imported nodes. """
Nodes = []
basedir, filename = os.path.split(path)
rootModule = os.path.basename(basedir)

for root, dirs, files in os.walk(basedir, topdown=False):
if root not in sys.path:
sys.path.append(root)

for name in files:
if name.endswith('.py') and not name.startswith('_'):
module_name = name[:-3]
module = importlib.import_module(module_name)
for name, obj in inspect.getmembers(module):
if inspect.isclass(obj) and not obj.__name__ == 'BaseNode':
for clsObj in inspect.getmro(obj):
if clsObj.__name__ == 'BaseNode':
Nodes.append(obj)
break
module_name = root.split(rootModule)[1].replace('\\', '.') + name[:-3]
modulePath = os.path.join(root, name)
froms = detectNodesFromText(modulePath)
if not froms:
continue

try:
mod = __import__(module_name, globals(), locals(), froms, 0)
for node in froms:
Nodes.append(getattr(mod, node))

except ImportError as e:
print ('Error in importing class: %s' % (e))
continue

return Nodes


Expand Down
3 changes: 2 additions & 1 deletion example_nodes/input_nodes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from NodeGraphQt import BaseNode, QtCore


Expand Down Expand Up @@ -64,7 +65,7 @@ def run(self):
data = fread.read()
self.set_property('output', data)
else:
print('No existe %s' % path)
print("%s doesn't exist!" % path)
self.set_property('output', '')


Expand Down
36 changes: 18 additions & 18 deletions example.nodes → example_nodes/networks/example.nodes
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"nodes":{
"0x15095e08":{
"0x150eccc8":{
"type_":"Viewers.DataViewerNode",
"icon":null,
"name":"Output",
Expand All @@ -27,14 +27,14 @@
"width":170,
"height":91.0,
"pos":[
149.0,
-146.0
602.4100268187593,
341.7567828173967
],
"custom":{
"data":""
}
},
"0x15126848":{
"0x150f5588":{
"type_":"Inputs.BoolInputNode",
"icon":null,
"name":"Bool",
Expand All @@ -61,15 +61,15 @@
"width":170,
"height":103.0,
"pos":[
-413.0,
-212.0
40.41002681875926,
275.7567828173967
],
"custom":{
"out":true,
"combo":"True"
}
},
"0x15140308":{
"0x150f5d88":{
"type_":"Logics.BooleanNode",
"icon":null,
"name":"Boolean",
Expand All @@ -96,15 +96,15 @@
"width":170,
"height":103.0,
"pos":[
-143.0,
-149.0
310.41002681875943,
338.7567828173967
],
"custom":{
"out":null,
"funcs":"and"
}
},
"0x1514a4c8":{
"0x1510e948":{
"type_":"Inputs.BoolInputNode",
"icon":null,
"name":"Bool 1",
Expand All @@ -131,8 +131,8 @@
"width":170,
"height":103.0,
"pos":[
-411.25,
-61.5
42.16002681875926,
426.2567828173967
],
"custom":{
"out":true,
Expand All @@ -143,31 +143,31 @@
"connections":[
{
"in":[
"0x15095e08",
"0x150eccc8",
"data"
],
"out":[
"0x15140308",
"0x150f5d88",
"out"
]
},
{
"out":[
"0x15126848",
"0x150f5588",
"out"
],
"in":[
"0x15140308",
"0x150f5d88",
"a"
]
},
{
"in":[
"0x15140308",
"0x150f5d88",
"b"
],
"out":[
"0x1514a4c8",
"0x1510e948",
"out"
]
}
Expand Down
Loading