Skip to content

Commit 4ca9abd

Browse files
committed
add node publish
1 parent 317f8d8 commit 4ca9abd

File tree

15 files changed

+5316
-2165
lines changed

15 files changed

+5316
-2165
lines changed

NodeGraphQt/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def __init__(self):
9494
# widgets
9595
from .widgets.node_tree import NodeTreeWidget
9696
from .widgets.properties_bin import PropertiesBinWidget
97+
from .widgets.node_publish_widget import NodePublishWidget
9798

9899
__version__ = VERSION
99100
__all__ = [
@@ -110,6 +111,7 @@ def __init__(self):
110111
'PropertiesBinWidget',
111112
'VERSION',
112113
'constants',
113-
'setup_context_menu'
114+
'setup_context_menu',
115+
'NodePublishWidget'
114116
]
115117

NodeGraphQt/base/graph.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ def register_node(self, node, alias=None):
717717
alias (str): custom alias name for the node type.
718718
"""
719719
self._node_factory.register_node(node, alias)
720+
self._viewer.rebuild_tab_search()
720721

721722
def create_node(self, node_type, name=None, selected=True, color=None,
722723
text_color=None, pos=None):
@@ -815,7 +816,6 @@ def add_node(self, node, pos=None, unique_name=True):
815816
for pname, pattrs in prop_attrs.items():
816817
node_attrs[node.type_][pname].update(pattrs)
817818
self.model.set_node_common_properties(node_attrs)
818-
819819
node.set_graph(self)
820820
if unique_name:
821821
node.NODE_NAME = self.get_unique_name(node.NODE_NAME)
@@ -1072,9 +1072,13 @@ def _serialize(self, nodes):
10721072
node_dict = n.model.to_dict
10731073

10741074
if isinstance(n, SubGraph):
1075-
children = n.children()
1076-
if children:
1077-
node_dict[n.model.id]['sub_graph'] = self._serialize(children)
1075+
published = n.has_property('published')
1076+
if published:
1077+
published = n.get_property('published')
1078+
if not published:
1079+
children = n.children()
1080+
if children:
1081+
node_dict[n.model.id]['sub_graph'] = self._serialize(children)
10781082

10791083
nodes_data.update(node_dict)
10801084

@@ -1135,13 +1139,18 @@ def _deserialize(self, data, relative_pos=False, pos=None, set_parent=True):
11351139
node.model.set_property(prop, val)
11361140
nodes[n_id] = node
11371141
self.add_node(node, n_data.get('pos'), unique_name=set_parent)
1138-
node.set_graph(self)
1142+
# node.set_graph(self)
11391143

11401144
if isinstance(node, SubGraph):
1141-
sub_graph = n_data.get('sub_graph', None)
1142-
if sub_graph:
1143-
children = self._deserialize(sub_graph, relative_pos, pos, False)
1144-
[child.set_parent(node) for child in children]
1145+
if n_data.get('custom', None):
1146+
published = n_data['custom'].get('published', False)
1147+
else:
1148+
published = False
1149+
if not published:
1150+
sub_graph = n_data.get('sub_graph', None)
1151+
if sub_graph:
1152+
children = self._deserialize(sub_graph, relative_pos, pos, False)
1153+
[child.set_parent(node) for child in children]
11451154

11461155
if n_data.get('dynamic_port', None):
11471156
node.set_ports({'input_ports': n_data['input_ports'], 'output_ports': n_data['output_ports']})
@@ -1203,18 +1212,21 @@ def save_session(self, file_path):
12031212
Args:
12041213
file_path (str): path to the saved node layout.
12051214
"""
1215+
12061216
root_node = self.root_node()
12071217
if root_node is not None:
12081218
nodes = root_node.children()
12091219
else:
12101220
nodes = self.all_nodes()
1221+
12111222
serialized_data = self._serialize(nodes)
12121223

12131224
node_space = self.get_node_space()
12141225
if node_space is not None:
12151226
node_space = node_space.id
12161227
serialized_data['graph'] = {'node_space': node_space, 'pipe_layout': self._viewer.get_pipe_layout()}
12171228
serialized_data['graph']['graph_rect'] = self._viewer.scene_rect()
1229+
12181230
file_path = file_path.strip()
12191231
with open(file_path, 'w') as file_out:
12201232
json.dump(serialized_data, file_out, indent=2, separators=(',', ':'))

NodeGraphQt/constants.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,20 @@
7171
NODE_PROP_SLIDER = 9
7272
#: Property type represented with a file selector widget in the properties bin.
7373
NODE_PROP_FILE = 10
74+
#: Property type represented with a file save widget in the properties bin.
75+
NODE_PROP_FILE_SAVE = 11
7476
#: Property type represented with a vector2 widget in the properties bin.
75-
NODE_PROP_VECTOR2 = 11
77+
NODE_PROP_VECTOR2 = 12
7678
#: Property type represented with vector3 widget in the properties bin.
77-
NODE_PROP_VECTOR3 = 12
79+
NODE_PROP_VECTOR3 = 13
7880
#: Property type represented with vector4 widget in the properties bin.
79-
NODE_PROP_VECTOR4 = 13
81+
NODE_PROP_VECTOR4 = 14
8082
#: Property type represented with float widget in the properties bin.
81-
NODE_PROP_FLOAT = 14
83+
NODE_PROP_FLOAT = 15
8284
#: Property type represented with int widget in the properties bin.
83-
NODE_PROP_INT = 15
85+
NODE_PROP_INT = 16
8486
#: Property type represented with button widget in the properties bin.
85-
NODE_PROP_BUTTON = 16
87+
NODE_PROP_BUTTON = 17
8688

8789
# === NODE VIEWER ===
8890

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# from .. import QtWidgets
2+
from PySide2 import QtWidgets
3+
import os
4+
from NodeGraphQt.widgets.properties import PropFileSavePath
5+
6+
7+
class _element_widget(QtWidgets.QWidget):
8+
def __init__(self):
9+
super(_element_widget, self).__init__()
10+
self.layout = QtWidgets.QHBoxLayout(self)
11+
12+
13+
class NodePublishWidget(QtWidgets.QDialog):
14+
def __init__(self, node):
15+
super(NodePublishWidget, self).__init__()
16+
self.setWindowTitle("Publish Node")
17+
self.published = False
18+
19+
layout = QtWidgets.QVBoxLayout(self)
20+
self.layout = QtWidgets.QHBoxLayout(self)
21+
self.node = node
22+
self.node_name = QtWidgets.QLineEdit(node.name())
23+
self.node_class_name = QtWidgets.QLineEdit(node.name().replace(" ", "_"))
24+
self.node_identifier = QtWidgets.QLineEdit('Custom')
25+
26+
path = os.getcwd() + "/example_auto_nodes/published_nodes"
27+
if not os.path.exists(path):
28+
path = os.getcwd()
29+
path += "/" + self.node_class_name.text() + ".node"
30+
path = path.replace('\\', '/')
31+
32+
self.file_path_widget = PropFileSavePath()
33+
self.file_path_widget.set_file_dir(path)
34+
self.file_path_widget.set_ext('*.json;*.node')
35+
self.file_path_widget.set_value(path)
36+
37+
publish_btn = QtWidgets.QPushButton('Publish')
38+
publish_btn.pressed.connect(lambda: self.publish())
39+
40+
cancel_btn = QtWidgets.QPushButton('Cancel')
41+
cancel_btn.pressed.connect(lambda: self.close())
42+
43+
name_widget = _element_widget()
44+
name_widget.layout.addWidget(QtWidgets.QLabel('Node Name'))
45+
name_widget.layout.addWidget(self.node_name)
46+
47+
identifier_widget = _element_widget()
48+
identifier_widget.layout.addWidget(QtWidgets.QLabel('Node Identifier'))
49+
identifier_widget.layout.addWidget(self.node_identifier)
50+
51+
class_name_widget = _element_widget()
52+
class_name_widget.layout.addWidget(QtWidgets.QLabel('Node Class Name'))
53+
class_name_widget.layout.addWidget(self.node_class_name)
54+
55+
layout.addWidget(name_widget)
56+
layout.addWidget(class_name_widget)
57+
layout.addWidget(identifier_widget)
58+
layout.addWidget(self.file_path_widget)
59+
layout.addWidget(publish_btn)
60+
layout.addWidget(cancel_btn)
61+
62+
layout.addStretch()
63+
64+
def publish(self):
65+
if self.published:
66+
return
67+
file_path = self.file_path_widget.get_value()
68+
node_name = self.node_name.text()
69+
node_identifier = self.node_identifier.text()
70+
node_class_name = self.node_class_name.text()
71+
self.node.publish(file_path, node_name, node_identifier, node_class_name)
72+
self.published = True
73+
self.close()
74+
75+
76+
if __name__ == '__main__':
77+
import sys
78+
79+
class node(object):
80+
def __init__(self):
81+
pass
82+
83+
def name(self):
84+
return 'node name'
85+
86+
app = QtWidgets.QApplication()
87+
wid = NodePublishWidget(None, node())
88+
wid.show()
89+
sys.exit(app.exec_())

NodeGraphQt/widgets/properties.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
NODE_PROP_COLORPICKER,
1212
NODE_PROP_SLIDER,
1313
NODE_PROP_FILE,
14+
NODE_PROP_FILE_SAVE,
1415
NODE_PROP_VECTOR2,
1516
NODE_PROP_VECTOR3,
1617
NODE_PROP_VECTOR4,
@@ -289,12 +290,16 @@ def __init__(self, parent=None):
289290
self._ledit.setStyleSheet("QLineEdit{border:1px solid}")
290291
_button.setStyleSheet("QPushButton{border:1px solid}")
291292
self._ext = "*"
293+
self._file_dir = None
292294

293295
def set_ext(self, ext):
294296
self._ext = ext
295297

298+
def set_file_dir(self, dir):
299+
self._file_dir = dir
300+
296301
def _on_select_file(self):
297-
file_path = file_dialog.getOpenFileName(self, ext_filter=self._ext)
302+
file_path = file_dialog.getOpenFileName(self, file_dir=self._file_dir, ext_filter=self._ext)
298303
file = file_path[0] or None
299304
if file:
300305
self.set_value(file)
@@ -314,6 +319,17 @@ def set_value(self, value):
314319
self._on_value_change(_value)
315320

316321

322+
class PropFileSavePath(PropFilePath):
323+
def __init__(self, parent=None):
324+
super(PropFileSavePath, self).__init__(parent)
325+
326+
def _on_select_file(self):
327+
file_path = file_dialog.getSaveFileName(self, file_dir=self._file_dir, ext_filter=self._ext)
328+
file = file_path[0] or None
329+
if file:
330+
self.set_value(file)
331+
332+
317333
class _valueMenu(QtWidgets.QMenu):
318334
mouseMove = QtCore.Signal(object)
319335
mouseRelease = QtCore.Signal(object)
@@ -676,6 +692,7 @@ def get_value(self):
676692
NODE_PROP_COLORPICKER: PropColorPicker,
677693
NODE_PROP_SLIDER: PropSlider,
678694
NODE_PROP_FILE: PropFilePath,
695+
NODE_PROP_FILE_SAVE: PropFileSavePath,
679696
NODE_PROP_VECTOR2: PropVector2,
680697
NODE_PROP_VECTOR3: PropVector3,
681698
NODE_PROP_VECTOR4: PropVector4,

NodeGraphQt/widgets/tab_search.py

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -125,37 +125,39 @@ def fuzzyFinder(key, collection):
125125
return [x for _, _, x in sorted(suggestions)]
126126

127127

128-
class TabSearchMenuWidget(QtWidgets.QLineEdit):
128+
class TabSearchMenuWidget(QtWidgets.QMenu):
129129
search_submitted = QtCore.Signal(str)
130130

131-
def __init__(self, parent=None, node_dict=None):
132-
super(TabSearchMenuWidget, self).__init__(parent)
133-
self.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)
134-
self.setStyleSheet(STYLE_TABSEARCH)
135-
self.setMinimumSize(200, 22)
136-
self.setTextMargins(2, 0, 2, 0)
131+
def __init__(self, node_dict=None):
132+
super(TabSearchMenuWidget, self).__init__()
133+
134+
self.line_edit = QtWidgets.QLineEdit()
135+
self.line_edit.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)
136+
self.line_edit.setStyleSheet(STYLE_TABSEARCH)
137+
self.line_edit.setMinimumSize(200, 22)
138+
self.line_edit.setTextMargins(2, 0, 2, 0)
137139

138140
self._node_dict = node_dict or {}
139141
if self._node_dict:
140142
self._generate_items_from_node_dict()
141143

142-
self.SearchMenu = QtWidgets.QMenu()
143-
searchWidget = QtWidgets.QWidgetAction(self)
144-
searchWidget.setDefaultWidget(self)
145-
self.SearchMenu.addAction(searchWidget)
146-
self.SearchMenu.setStyleSheet(STYLE_QMENU)
144+
searchWidget = QtWidgets.QWidgetAction(self.line_edit)
145+
searchWidget.setDefaultWidget(self.line_edit)
146+
self.addAction(searchWidget)
147+
self.setStyleSheet(STYLE_QMENU)
147148

148149
self._actions = {}
149150
self._menus = {}
150151
self._searched_actions = []
151152

152-
self.returnPressed.connect(self._on_search_submitted)
153-
self.textChanged.connect(self._on_text_changed)
153+
self.line_edit.returnPressed.connect(self._on_search_submitted)
154+
self.line_edit.textChanged.connect(self._on_text_changed)
155+
self.rebuild = False
154156

155157
def __repr__(self):
156158
return '<{} at {}>'.format(self.__class__.__name__, hex(id(self)))
157159

158-
def _on_text_changed(self,text):
160+
def _on_text_changed(self, text):
159161
self._clear_actions()
160162

161163
if not text:
@@ -167,32 +169,32 @@ def _on_text_changed(self,text):
167169
action_names = fuzzyFinder(text, self._actions.keys())
168170

169171
self._searched_actions = [self._actions[name] for name in action_names]
170-
self.SearchMenu.addActions(self._searched_actions)
172+
self.addActions(self._searched_actions)
171173

172174
if self._searched_actions:
173-
self.SearchMenu.setActiveAction(self._searched_actions[0])
175+
self.setActiveAction(self._searched_actions[0])
174176

175177
def _clear_actions(self):
176-
for action in self._searched_actions:
177-
self.SearchMenu.removeAction(action)
178+
[self.removeAction(action) for action in self._searched_actions]
178179
self._searched_actions = []
179180

180181
def _set_menu_visible(self, visible):
181182
[menu.menuAction().setVisible(visible) for menu in self._menus.values()]
182183

183184
def _close(self):
184185
self._set_menu_visible(False)
185-
self.SearchMenu.setVisible(False)
186-
self.SearchMenu.menuAction().setVisible(False)
186+
self.setVisible(False)
187+
self.menuAction().setVisible(False)
187188

188189
def _show(self):
189-
self.setText("")
190-
self.setFocus()
190+
self.line_edit.setText("")
191+
self.line_edit.setFocus()
191192
self._set_menu_visible(True)
192-
self.SearchMenu.exec_(QtGui.QCursor.pos())
193+
self.exec_(QtGui.QCursor.pos())
193194

194195
def _on_search_submitted(self):
195196
action = self.sender()
197+
print(action)
196198
if type(action) is not QtWidgets.QAction:
197199
if len(self._searched_actions) > 0:
198200
action = self._searched_actions[0]
@@ -237,7 +239,7 @@ def build_menu_tree(self):
237239
for menu_path, menu in menus.items():
238240
self._menus[menu_path] = menu
239241
if i == 0:
240-
self.SearchMenu.addMenu(menu)
242+
self.addMenu(menu)
241243
else:
242244
parentMenu = self._menus[menu.parentPath]
243245
parentMenu.addMenu(menu)
@@ -254,18 +256,24 @@ def build_menu_tree(self):
254256
if menu_path in self._menus.keys():
255257
self._menus[menu_path].addAction(action)
256258
else:
257-
self.SearchMenu.addAction(action)
259+
self.addAction(action)
258260

259261
def set_nodes(self, node_dict=None):
260-
if not self._node_dict:
262+
if not self._node_dict or self.rebuild:
261263
self._node_dict.clear()
264+
self._clear_actions()
265+
self._set_menu_visible(False)
266+
[self.removeAction(menu.menuAction()) for menu in self._menus.values()]
267+
self._actions.clear()
268+
self._menus.clear()
262269
for name, node_types in node_dict.items():
263270
if len(node_types) == 1:
264271
self._node_dict[name] = node_types[0]
265272
continue
266273
for node_id in node_types:
267274
self._node_dict['{} ({})'.format(name, node_id)] = node_id
268275
self.build_menu_tree()
276+
self.rebuild = False
269277

270278
self._show()
271279

0 commit comments

Comments
 (0)