Skip to content

Commit f1ae002

Browse files
authored
Merge pull request #103 from jchanvfx/nodes_list_widget
Nodes list widget
2 parents 0e537aa + 821e84d commit f1ae002

File tree

6 files changed

+151
-11
lines changed

6 files changed

+151
-11
lines changed

NodeGraphQt/base/graph.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
23
import json
34
import os
45
import re
@@ -13,6 +14,8 @@
1314
from NodeGraphQt.base.model import NodeGraphModel
1415
from NodeGraphQt.base.node import NodeObject
1516
from NodeGraphQt.base.port import Port
17+
from NodeGraphQt.constants import DRAG_DROP_ID
18+
from NodeGraphQt.widgets.node_list import NodeListWidget
1619
from NodeGraphQt.widgets.properties_bin import PropertiesBinWidget
1720
from NodeGraphQt.widgets.viewer import NodeViewer
1821

@@ -45,7 +48,9 @@ def __init__(self, parent=None, tab_search_key='tab'):
4548
self._viewer = NodeViewer(parent)
4649
self._node_factory = NodeFactory()
4750
self._undo_stack = QtWidgets.QUndoStack(self)
48-
self._properties_bin = PropertiesBinWidget()
51+
52+
self._properties_bin = None
53+
self._nodes_list = None
4954

5055
tab = QtWidgets.QAction('Search Nodes', self)
5156
tab.setShortcut(tab_search_key)
@@ -65,10 +70,6 @@ def _wire_signals(self):
6570
self._viewer.node_selected.connect(self._on_node_selected)
6671
self._viewer.data_dropped.connect(self._on_node_data_dropped)
6772

68-
# wire up properties bin widget.
69-
self._properties_bin.property_changed.connect(
70-
self._on_property_changed)
71-
7273
def _toggle_tab_search(self):
7374
"""
7475
toggle the tab search widget.
@@ -99,7 +100,8 @@ def _on_node_double_clicked(self, node_id):
99100
node_id (str): node id emitted by the viewer.
100101
"""
101102
node = self.get_node_by_id(node_id)
102-
self._properties_bin.add_node(node)
103+
if self._properties_bin:
104+
self._properties_bin.add_node(node)
103105

104106
self.node_double_clicked.emit(node)
105107

@@ -122,6 +124,18 @@ def _on_node_data_dropped(self, data, pos):
122124
data (QtCore.QMimeData): mime data.
123125
pos (QtCore.QPoint): scene position relative to the drop.
124126
"""
127+
128+
# don't emit signal for internal widget drops.
129+
if data.hasFormat('text/plain'):
130+
if data.text().startswith('<${}>:'.format(DRAG_DROP_ID)):
131+
node_ids = data.text()[len('<${}>:'.format(DRAG_DROP_ID)):]
132+
x, y = pos.x(), pos.y()
133+
for node_id in node_ids.split(','):
134+
self.create_node(node_id, pos=[x, y])
135+
x += 20
136+
y += 20
137+
return
138+
125139
self.data_dropped.emit(data, pos)
126140

127141
def _on_nodes_moved(self, node_data):
@@ -222,13 +236,31 @@ def scene(self):
222236

223237
def properties_bin(self):
224238
"""
225-
Return the node properties bin widget.
239+
Initializes the node properties bin widget when first called.
226240
227241
Returns:
228-
PropBinWidget: widget.
229-
"""
242+
PropBinWidget: the initialized widget.
243+
"""
244+
if self._properties_bin is None:
245+
self._properties_bin = PropertiesBinWidget()
246+
# wire up widget.
247+
self._properties_bin.property_changed.connect(
248+
self._on_property_changed
249+
)
230250
return self._properties_bin
231251

252+
def nodes_list(self):
253+
"""
254+
Initializes the nodes list widget when first called.
255+
256+
Returns:
257+
NodeListWidget: the initialized widget.
258+
"""
259+
if self._nodes_list is None:
260+
self._nodes_list = NodeListWidget()
261+
self._nodes_list.set_node_factory(self._node_factory)
262+
return self._nodes_list
263+
232264
def undo_stack(self):
233265
"""
234266
Returns the undo stack used in the node graph

NodeGraphQt/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565

6666
SCENE_AREA = 8000.0
6767

68+
DRAG_DROP_ID = 'n0deGraphQT'
69+
6870
# === PATHS ===
6971

7072
BASE_PATH = os.path.dirname(os.path.abspath(__file__))

NodeGraphQt/widgets/node_list.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
from NodeGraphQt import QtWidgets, QtCore
4+
from NodeGraphQt.constants import DRAG_DROP_ID
5+
6+
7+
TYPE_NODE = QtWidgets.QTreeWidgetItem.UserType + 1
8+
TYPE_CATEGORY = QtWidgets.QTreeWidgetItem.UserType + 2
9+
10+
11+
class BaseListWidgetItem(QtWidgets.QListWidgetItem):
12+
13+
def __eq__(self, other):
14+
return id(self) == id(other)
15+
16+
17+
class NodeListWidget(QtWidgets.QTreeWidget):
18+
19+
def __init__(self, parent=None):
20+
super(NodeListWidget, self).__init__(parent)
21+
self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly)
22+
self.setHeaderHidden(True)
23+
self._factory = None
24+
self._custom_labels = {}
25+
26+
def mimeData(self, items):
27+
node_ids = ','.join(i.toolTip(0) for i in items)
28+
mime_data = super(NodeListWidget, self).mimeData(items)
29+
mime_data.setText('<${}>:{}'.format(DRAG_DROP_ID, node_ids))
30+
return mime_data
31+
32+
def _build_tree(self):
33+
"""
34+
Populate the node tree.
35+
"""
36+
self.clear()
37+
categories = set()
38+
node_types = {}
39+
for name, node_ids in self._factory.names.items():
40+
for nid in node_ids:
41+
categories.add('.'.join(nid.split('.')[:-1]))
42+
node_types[nid] = name
43+
44+
category_items = {}
45+
for category in sorted(categories):
46+
if category in self._custom_labels.keys():
47+
label = self._custom_labels[category]
48+
else:
49+
label = '- {}'.format(category)
50+
cat_item = QtWidgets.QTreeWidgetItem(
51+
self, [label], type=TYPE_CATEGORY
52+
)
53+
cat_item.setFirstColumnSpanned(True)
54+
cat_item.setFlags(QtCore.Qt.ItemIsEnabled)
55+
self.addTopLevelItem(cat_item)
56+
cat_item.setExpanded(True)
57+
category_items[category] = cat_item
58+
59+
for node_id, node_name in node_types.items():
60+
category = '.'.join(node_id.split('.')[:-1])
61+
category_item = category_items[category]
62+
63+
item = QtWidgets.QTreeWidgetItem(
64+
category_item, [node_name], type=TYPE_NODE
65+
)
66+
item.setToolTip(0, node_id)
67+
68+
category_item.addChild(item)
69+
70+
71+
def set_node_factory(self, factory):
72+
"""
73+
Set current node factory.
74+
75+
Args:
76+
factory (NodeFactory): node factory.
77+
"""
78+
self._factory = factory
79+
80+
def set_category_label(self, category, label):
81+
"""
82+
Set custom display label for a node category.
83+
84+
Args:
85+
category (str): node identifier category eg. "nodeGraphQt.nodes"
86+
label (str): custom display label.
87+
"""
88+
self._custom_labels[category] = label
89+
90+
def update(self):
91+
"""
92+
Update and refresh the node list widget.
93+
"""
94+
self._build_tree()

NodeGraphQt/widgets/viewer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
23
import os
34

45
from NodeGraphQt import QtGui, QtCore, QtWidgets

example.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,25 @@ def __init__(self):
4444
viewer.resize(1100, 800)
4545
viewer.show()
4646

47+
4748
# show the properties bin when a node is "double clicked" in the graph.
4849
properties_bin = graph.properties_bin()
4950
properties_bin.setWindowFlags(QtCore.Qt.Tool)
50-
5151
def show_prop_bin(node):
5252
if not properties_bin.isVisible():
5353
properties_bin.show()
54-
5554
graph.node_double_clicked.connect(show_prop_bin)
5655

56+
57+
# show the nodes list when a node is "double clicked" in the graph.
58+
node_list = graph.nodes_list()
59+
def show_nodes_list(node):
60+
if not node_list.isVisible():
61+
node_list.update()
62+
node_list.show()
63+
graph.node_double_clicked.connect(show_nodes_list)
64+
65+
5766
# registered nodes.
5867
reg_nodes = [
5968
Backdrop, MyNode,

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
PySide2>=5.12
2+
Qt.py>=1.2.0.b2
3+
python>=3.6

0 commit comments

Comments
 (0)