Skip to content

Commit 4d18d59

Browse files
committed
add node space bar
1 parent bcfba66 commit 4d18d59

File tree

7 files changed

+326
-79
lines changed

7 files changed

+326
-79
lines changed

NodeGraphQt/base/graph.py

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,21 @@
2121
PIPE_LAYOUT_ANGLE,
2222
IN_PORT, OUT_PORT)
2323
from ..widgets.viewer import NodeViewer
24+
from ..widgets.node_space_bar import node_space_bar
2425

2526

2627
class QWidgetDrops(QtWidgets.QWidget):
28+
def __init__(self):
29+
super(QWidgetDrops, self).__init__()
30+
self.setAcceptDrops(True)
31+
self.setWindowTitle("NodeGraphQt")
32+
self.setStyleSheet('''
33+
QWidget {
34+
background-color: rgb(55,55,55);
35+
color: rgb(200,200,200);
36+
border-width: 0px;
37+
}''')
38+
2739
def dragEnterEvent(self, event):
2840
if event.mimeData().hasUrls:
2941
event.accept()
@@ -135,7 +147,7 @@ def __init__(self, parent=None):
135147
self._viewer.need_show_tab_search.connect(self._toggle_tab_search)
136148

137149
self._wire_signals()
138-
self.widget.setAcceptDrops(True)
150+
self._node_space_bar = node_space_bar(self)
139151

140152
def __repr__(self):
141153
return '<{} object at {}>'.format(self.__class__.__name__, hex(id(self)))
@@ -358,6 +370,7 @@ def widget(self):
358370

359371
layout = QtWidgets.QVBoxLayout(self._widget)
360372
layout.setContentsMargins(0, 0, 0, 0)
373+
layout.addWidget(self._node_space_bar)
361374
layout.addWidget(self._viewer)
362375
return self._widget
363376

@@ -631,7 +644,12 @@ def fit_to_selection(self):
631644
Sets the zoom level to fit selected nodes.
632645
If no nodes are selected then all nodes in the graph will be framed.
633646
"""
634-
nodes = self.selected_nodes() or self.all_nodes()
647+
if self._current_node_space is None:
648+
all_nodes = self.all_nodes()
649+
else:
650+
all_nodes = self._current_node_space.children()
651+
652+
nodes = self.selected_nodes() or all_nodes
635653
if not nodes:
636654
return
637655
self._viewer.zoom_to_nodes([n.view for n in nodes])
@@ -720,7 +738,6 @@ def create_node(self, node_type, name=None, selected=True, color=None,
720738
NodeCls = self._node_factory.create_node_instance(node_type)
721739
if NodeCls:
722740
node = NodeCls()
723-
node.set_graph(self)
724741
node.model._graph_model = self.model
725742

726743
wid_types = node.model.__dict__.pop('_TEMP_property_widget_types')
@@ -737,6 +754,7 @@ def create_node(self, node_type, name=None, selected=True, color=None,
737754
node.NODE_NAME = self.get_unique_name(name or node.NODE_NAME)
738755
node.model.name = node.NODE_NAME
739756
node.model.selected = selected
757+
node.set_graph(self)
740758

741759
def format_color(clr):
742760
if isinstance(clr, str):
@@ -758,7 +776,15 @@ def format_color(clr):
758776

759777
undo_cmd = NodeAddedCmd(self, node, node.model.pos)
760778
undo_cmd.setText('create node: "{}"'.format(node.NODE_NAME))
761-
self._undo_stack.push(undo_cmd)
779+
780+
if isinstance(node, SubGraph):
781+
self.begin_undo('create sub graph node')
782+
self._undo_stack.push(undo_cmd)
783+
if node.get_property('create_from_select'):
784+
node.create_from_nodes(self.selected_nodes())
785+
self.end_undo()
786+
else:
787+
self._undo_stack.push(undo_cmd)
762788
self.node_created.emit(node)
763789
return node
764790
raise Exception('\n\n>> Cannot find node:\t"{}"\n'.format(node_type))
@@ -798,14 +824,15 @@ def set_node_space(self, node):
798824
Args:
799825
node (NodeGraphQt.SubGraph): node object.
800826
"""
827+
if node is self._current_node_space:
828+
return
801829
if self._current_node_space is not None:
802-
[n.hide() for n in self._current_node_space.children()]
803830
self._current_node_space.exit()
804831

805832
self._current_node_space = node
806833
if node is not None:
807-
[n.show() for n in node.children()]
808834
node.enter()
835+
self._node_space_bar.set_node(node)
809836

810837
def get_node_space(self):
811838
"""
@@ -826,9 +853,13 @@ def delete_node(self, node):
826853
assert isinstance(node, NodeObject), \
827854
'node must be a instance of a NodeObject.'
828855
self.nodes_deleted.emit([node.id])
829-
self._undo_stack.push(NodeRemovedCmd(self, node))
830-
if isinstance(node,SubGraph):
856+
if isinstance(node, SubGraph):
857+
self._undo_stack.beginMacro('delete sub graph')
831858
self.delete_nodes(node.children())
859+
self._undo_stack.push(NodeRemovedCmd(self, node))
860+
self._undo_stack.endMacro()
861+
else:
862+
self._undo_stack.push(NodeRemovedCmd(self, node))
832863

833864
def delete_nodes(self, nodes):
834865
"""
@@ -839,7 +870,7 @@ def delete_nodes(self, nodes):
839870
"""
840871
self.nodes_deleted.emit([n.id for n in nodes])
841872
self._undo_stack.beginMacro('delete nodes')
842-
[self.delete_nodes(n.children()) for n in nodes if isinstance(n,SubGraph)]
873+
[self.delete_nodes(n.children()) for n in nodes if isinstance(n, SubGraph)]
843874
[self._undo_stack.push(NodeRemovedCmd(self, n)) for n in nodes]
844875
self._undo_stack.endMacro()
845876

@@ -880,17 +911,18 @@ def select_all(self):
880911
Select all nodes in the node graph.
881912
"""
882913
self._undo_stack.beginMacro('select all')
883-
for node in self.all_nodes():
884-
node.set_selected(True)
914+
if self._current_node_space is not None:
915+
[node.set_selected(True) for node in self._current_node_space.children()]
916+
else:
917+
[node.set_selected(True) for node in self.all_nodes()]
885918
self._undo_stack.endMacro()
886919

887920
def clear_selection(self):
888921
"""
889922
Clears the selection in the node graph.
890923
"""
891924
self._undo_stack.beginMacro('clear selection')
892-
for node in self.all_nodes():
893-
node.set_selected(False)
925+
[node.set_selected(False) for node in self.all_nodes()]
894926
self._undo_stack.endMacro()
895927

896928
def get_node_by_id(self, node_id=None):
@@ -905,6 +937,41 @@ def get_node_by_id(self, node_id=None):
905937
"""
906938
return self._model.nodes.get(node_id, None)
907939

940+
def get_node_by_path(self, node_path):
941+
"""
942+
Returns the node from the node path string.
943+
944+
Args:
945+
node_path (str): node path (:attr:`NodeObject.path()`)
946+
947+
Returns:
948+
NodeGraphQt.NodeObject: node object.
949+
"""
950+
names = [name for name in node_path.split("/") if name]
951+
root = names.pop(0)
952+
node = self._current_node_space
953+
if node is None:
954+
node = self.get_node_by_name(root)
955+
if node is None:
956+
return None
957+
else:
958+
while True:
959+
parent_node = node.parent()
960+
if parent_node is None:
961+
break
962+
node = parent_node
963+
964+
for name in names:
965+
find = False
966+
for n in node.children():
967+
if n.name() == name:
968+
node = n
969+
find = True
970+
continue
971+
if not find:
972+
return None
973+
return node
974+
908975
def get_node_by_name(self, name):
909976
"""
910977
Returns node that matches the name.
@@ -914,9 +981,14 @@ def get_node_by_name(self, name):
914981
Returns:
915982
NodeGraphQt.NodeObject: node object.
916983
"""
917-
for node_id, node in self._model.nodes.items():
984+
if self._current_node_space is not None:
985+
nodes = self._current_node_space.children()
986+
else:
987+
nodes = self.all_nodes()
988+
for node in nodes:
918989
if node.name() == name:
919990
return node
991+
return None
920992

921993
def get_unique_name(self, name):
922994
"""
@@ -929,7 +1001,10 @@ def get_unique_name(self, name):
9291001
str: unique node name.
9301002
"""
9311003
name = ' '.join(name.split())
932-
node_names = [n.name() for n in self.all_nodes()]
1004+
if self._current_node_space is not None:
1005+
node_names = [n.name() for n in self._current_node_space.children()]
1006+
else:
1007+
node_names = [n.name() for n in self.all_nodes()]
9331008
if name not in node_names:
9341009
return name
9351010

@@ -1149,13 +1224,15 @@ def import_session(self, file_path):
11491224
return
11501225

11511226
self._deserialize(layout_data)
1152-
node_space_id = layout_data['graph']['node_space']
1227+
1228+
if 'graph' in layout_data.keys():
1229+
node_space_id = layout_data['graph']['node_space']
11531230

1154-
# deserialize graph data
1155-
self.set_node_space(self.get_node_by_id(node_space_id))
1156-
self._viewer.set_pipe_layout(layout_data['graph']['pipe_layout'])
1231+
# deserialize graph data
1232+
self.set_node_space(self.get_node_by_id(node_space_id))
1233+
self._viewer.set_pipe_layout(layout_data['graph']['pipe_layout'])
11571234

1158-
self._viewer.set_scene_rect(layout_data['graph']['graph_rect'])
1235+
self._viewer.set_scene_rect(layout_data['graph']['graph_rect'])
11591236

11601237
self._undo_stack.clear()
11611238
self._model.session = file_path

NodeGraphQt/base/node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,9 @@ def path(self):
467467
str: current node path.
468468
"""
469469
if self.parent_id is None:
470-
return "/"
470+
return "/" + self.name()
471471

472-
return self.parent().path() + self.name() + "/"
472+
return self.parent().path() + "/" + self.name()
473473

474474
def delete(self):
475475
"""
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from .. import QtWidgets, QtCore
2+
from . stylesheet import STYLE_SLASH_BUTTON, STYLE_NODE_BUTTON
3+
4+
5+
class node_space_bar(QtWidgets.QWidget):
6+
def __init__(self, graph):
7+
super(node_space_bar, self).__init__()
8+
self.setMaximumHeight(20)
9+
self.hbox = QtWidgets.QHBoxLayout(self)
10+
self.hbox.setContentsMargins(0, 0, 0, 0)
11+
self.hbox.setSpacing(0)
12+
self.graph = graph
13+
self.update()
14+
15+
def update(self):
16+
self.set_node(self.graph.get_node_space())
17+
18+
def add_slash(self):
19+
btn_slash = QtWidgets.QPushButton("/")
20+
btn_slash.setFixedWidth(7)
21+
btn_slash.setStyleSheet(STYLE_SLASH_BUTTON)
22+
btn_slash.setFixedHeight(20)
23+
self.hbox.addWidget(btn_slash, QtCore.Qt.AlignLeft)
24+
25+
def add_node(self, node):
26+
node_name = node.name()
27+
btn_node = QtWidgets.QPushButton(node_name)
28+
btn_node.setFixedWidth(len(node_name)*8)
29+
btn_node.setStyleSheet(STYLE_NODE_BUTTON)
30+
btn_node.setFixedHeight(20)
31+
btn_node.clicked.connect(lambda: self.graph.set_node_space(node))
32+
self.hbox.addWidget(btn_node, QtCore.Qt.AlignLeft)
33+
34+
def clear(self):
35+
while self.hbox.count():
36+
child = self.hbox.takeAt(0)
37+
if child.widget():
38+
child.widget().deleteLater()
39+
40+
def set_node(self, node):
41+
self.clear()
42+
if node is None:
43+
self.add_slash()
44+
return
45+
nodes = [node]
46+
while True:
47+
parent_node = node.parent()
48+
if parent_node is None:
49+
break
50+
nodes.append(parent_node)
51+
node = parent_node
52+
53+
for node in reversed(nodes):
54+
self.add_slash()
55+
self.add_node(node)
56+
57+
self.hbox.addStretch()

NodeGraphQt/widgets/stylesheet.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,32 @@
220220
background-color: rgb(55, 55, 55);
221221
color: rgb(200 ,200, 200);
222222
}
223-
'''
223+
'''
224+
225+
STYLE_SLASH_BUTTON = '''
226+
QPushButton {
227+
border-radius: 0px;
228+
border-width: 0px;
229+
font-size:15px;
230+
padding-left: 0px;
231+
padding-right: 0px;
232+
background-color:transparent;
233+
}
234+
'''
235+
STYLE_NODE_BUTTON = '''
236+
QPushButton {
237+
border-radius: 0px;
238+
border-width: 1px;
239+
font-size:15px;
240+
padding-left: 0px;
241+
padding-right: 0px;
242+
background-color: rgb(20,100,180);
243+
}
244+
QPushButton::pressed {
245+
border-style: inset;
246+
border-color: rgb(100,100,100);
247+
background-color: rgb(30,30,30);
248+
}
249+
QPushButton::hover {
250+
background-color: rgb(10,80,150);
251+
}'''

example_auto_nodes.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ def print_path(graph, node):
5656
print(node.path())
5757

5858

59+
def find_node(graph, node):
60+
print(graph.get_node_by_path(node.path()))
61+
62+
5963
if __name__ == '__main__':
6064
app = QtWidgets.QApplication([])
6165

@@ -100,8 +104,9 @@ def print_path(graph, node):
100104
node_menu.add_command('Cook Node', cook_node, node_class=AutoNode)
101105
node_menu.add_command('Toggle Auto Cook', toggle_auto_cook, node_class=AutoNode)
102106
node_menu.add_command('Print Path', print_path, node_class=AutoNode)
107+
node_menu.add_command('Find Node', find_node, node_class=AutoNode)
103108

104-
graph.create_node('Utility.RootGraph')
109+
graph.create_node('Utility.RootGraph', name='root', selected=False)
105110
mathNodeA = graph.create_node('Module.MathModuleNode',
106111
name='Math Functions A',
107112
color='#0a1e20',

0 commit comments

Comments
 (0)