Skip to content

Commit 37173f0

Browse files
authored
Merge pull request #324 from jchanvfx/circle_node_updates
Circle node updates
2 parents b055489 + 372eb5d commit 37173f0

File tree

5 files changed

+272
-11
lines changed

5 files changed

+272
-11
lines changed

NodeGraphQt/qgraphics/node_circle.py

Lines changed: 268 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/python
22
from Qt import QtCore, QtGui, QtWidgets
33

4-
from NodeGraphQt.constants import NodeEnum
4+
from NodeGraphQt.constants import NodeEnum, PortEnum
55
from NodeGraphQt.qgraphics.node_base import NodeItem
66

77

@@ -17,9 +17,202 @@ class CircleNodeItem(NodeItem):
1717
def __init__(self, name='circle', parent=None):
1818
super(CircleNodeItem, self).__init__(name, parent)
1919

20+
def _align_ports_horizontal(self, v_offset):
21+
width = self._width
22+
txt_offset = PortEnum.CLICK_FALLOFF.value - 2
23+
spacing = 1
24+
25+
node_center_y = self.boundingRect().center().y()
26+
node_center_y += v_offset
27+
28+
# adjust input position
29+
inputs = [p for p in self.inputs if p.isVisible()]
30+
if inputs:
31+
port_width = inputs[0].boundingRect().width()
32+
port_height = inputs[0].boundingRect().height()
33+
34+
count = len(inputs)
35+
if count > 2:
36+
is_odd = bool(count % 2)
37+
middle_idx = int(count / 2)
38+
39+
# top half
40+
port_x = (port_width / 2) * -1
41+
port_y = node_center_y - (port_height / 2)
42+
for idx, port in enumerate(reversed(inputs[:middle_idx])):
43+
if idx == 0:
44+
if is_odd:
45+
port_x += (port_width / 2) - (txt_offset / 2)
46+
port_y -= port_height + spacing
47+
else:
48+
port_y -= (port_height / 2) + spacing
49+
port.setPos(port_x, port_y)
50+
port_x += (port_width / 2) - (txt_offset / 2)
51+
port_y -= port_height + spacing
52+
53+
# bottom half
54+
port_x = (port_width / 2) * -1
55+
port_y = node_center_y - (port_height / 2)
56+
for idx, port in enumerate(inputs[middle_idx:]):
57+
if idx == 0:
58+
if not is_odd:
59+
port_y += (port_height / 2) + spacing
60+
port.setPos(port_x, port_y)
61+
port_x += (port_width / 2) - (txt_offset / 2)
62+
port_y += port_height + spacing
63+
64+
else:
65+
port_x = (port_width / 2) * -1
66+
port_y = node_center_y - (port_height / 2)
67+
inputs[0].setPos(port_x, port_y - (port_height / 2) + spacing)
68+
inputs[1].setPos(port_x, port_y + (port_height / 2) + spacing)
69+
70+
# adjust input text position
71+
for port, text in self._input_items.items():
72+
if port.isVisible():
73+
port_width = port.boundingRect().width()
74+
txt_x = port.pos().x() + port_width - txt_offset
75+
text.setPos(txt_x, port.y() - 1.5)
76+
77+
# adjust output position
78+
outputs = [p for p in self.outputs if p.isVisible()]
79+
if outputs:
80+
port_width = outputs[0].boundingRect().width()
81+
port_height = outputs[0].boundingRect().height()
82+
83+
count = len(outputs)
84+
if count > 2:
85+
is_odd = bool(count % 2)
86+
middle_idx = int(count / 2)
87+
88+
# top half
89+
port_x = width - (port_width / 2)
90+
port_y = node_center_y - (port_height / 2)
91+
for idx, port in enumerate(reversed(outputs[:middle_idx])):
92+
if idx == 0:
93+
if is_odd:
94+
port_x -= (port_width / 2) - (txt_offset / 2)
95+
port_y -= port_height + spacing
96+
else:
97+
port_y -= (port_height / 2) + spacing
98+
port.setPos(port_x, port_y)
99+
port_x -= (port_width / 2) - (txt_offset / 2)
100+
port_y -= port_height + spacing
101+
102+
# bottom half
103+
port_x = width - (port_width / 2)
104+
port_y = node_center_y - (port_height / 2)
105+
for idx, port in enumerate(outputs[middle_idx:]):
106+
if idx == 0:
107+
if not is_odd:
108+
port_y += (port_width / 2) - (txt_offset / 2)
109+
port.setPos(port_x, port_y)
110+
port_x -= (port_width / 2) - (txt_offset / 2)
111+
port_y += port_height + spacing
112+
else:
113+
port_x = width - (port_width / 2)
114+
port_y = node_center_y - (port_height / 2)
115+
outputs[0].setPos(port_x, port_y - (port_height / 2) + spacing)
116+
outputs[1].setPos(port_x, port_y + (port_height / 2) + spacing)
117+
118+
# adjust output text position
119+
for port, text in self._output_items.items():
120+
if port.isVisible():
121+
txt_width = text.boundingRect().width() - txt_offset
122+
txt_x = port.x() - txt_width
123+
text.setPos(txt_x, port.y() - 1.5)
124+
125+
def _align_ports_vertical(self, v_offset):
126+
height = self._height
127+
node_center_x = self.boundingRect().center().x() + v_offset
128+
129+
# adjust input position
130+
inputs = [p for p in self.inputs if p.isVisible()]
131+
if inputs:
132+
port_width = inputs[0].boundingRect().width()
133+
port_height = inputs[0].boundingRect().height()
134+
135+
count = len(inputs)
136+
if count > 2:
137+
is_odd = bool(count % 2)
138+
middle_idx = int(count / 2)
139+
140+
delta = (self._width / (len(inputs) + 1)) / 2
141+
142+
# left half
143+
port_x = node_center_x - (port_width / 2)
144+
port_y = (port_height / 2) * -1
145+
for idx, port in enumerate(reversed(inputs[:middle_idx])):
146+
if idx == 0:
147+
if is_odd:
148+
port_x -= (port_width / 2) + delta
149+
port_y += (port_height / 2)
150+
else:
151+
port_x -= delta
152+
port.setPos(port_x, port_y)
153+
port_x -= (port_width / 2) + delta
154+
port_y += (port_height / 2)
155+
156+
# right half
157+
port_x = node_center_x - (port_width / 2)
158+
port_y = (port_height / 2) * -1
159+
for idx, port in enumerate(inputs[middle_idx:]):
160+
if idx == 0:
161+
if not is_odd:
162+
port_x += delta
163+
port.setPos(port_x, port_y)
164+
port_x += (port_width / 2) + delta
165+
port_y += (port_height / 2)
166+
167+
# adjust output position
168+
outputs = [p for p in self.outputs if p.isVisible()]
169+
if outputs:
170+
port_width = outputs[0].boundingRect().width()
171+
port_height = outputs[0].boundingRect().height()
172+
173+
count = len(outputs)
174+
if count > 2:
175+
is_odd = bool(count % 2)
176+
middle_idx = int(count / 2)
177+
178+
delta = (self._width / (len(outputs) + 1)) / 2
179+
180+
# left half
181+
port_x = node_center_x - (port_width / 2)
182+
port_y = height - (port_height / 2)
183+
for idx, port in enumerate(reversed(outputs[:middle_idx])):
184+
if idx == 0:
185+
if is_odd:
186+
port_x -= (port_width / 2) + delta
187+
port_y -= (port_height / 2)
188+
else:
189+
port_x -= delta
190+
port.setPos(port_x, port_y)
191+
port_x -= (port_width / 2) + delta
192+
port_y -= (port_height / 2)
193+
194+
# right half
195+
port_x = node_center_x - (port_width / 2)
196+
port_y = height - (port_height / 2)
197+
for idx, port in enumerate(outputs[middle_idx:]):
198+
if idx == 0:
199+
if not is_odd:
200+
port_x += delta
201+
port.setPos(port_x, port_y)
202+
port_x += (port_width / 2) + delta
203+
port_y -= (port_height / 2)
204+
20205
def _paint_horizontal(self, painter, option, widget):
21206
painter.save()
22207

208+
# display node area for debugging
209+
# ----------------------------------------------------------------------
210+
# pen = QtGui.QPen(QtGui.QColor(255, 255, 255, 80), 0.8)
211+
# pen.setStyle(QtCore.Qt.DotLine)
212+
# painter.setPen(pen)
213+
# painter.drawRect(self.boundingRect())
214+
# ----------------------------------------------------------------------
215+
23216
text_rect = self._text_item.boundingRect()
24217
text_width = text_rect.width()
25218
if text_width < 20.0:
@@ -60,7 +253,7 @@ def _paint_horizontal(self, painter, option, widget):
60253
)
61254
path = QtGui.QPainterPath()
62255
path.moveTo(pt1)
63-
path.lineTo(QtCore.QPointF(pt1.x() + 4.0, pt1.y()))
256+
# path.lineTo(QtCore.QPointF(pt1.x() + 4.0, pt1.y()))
64257
path.lineTo(rect.center())
65258
painter.drawPath(path)
66259

@@ -77,7 +270,7 @@ def _paint_horizontal(self, painter, option, widget):
77270
)
78271
path = QtGui.QPainterPath()
79272
path.moveTo(pt1)
80-
path.lineTo(QtCore.QPointF(pt1.x() - 2.0, pt1.y()))
273+
# path.lineTo(QtCore.QPointF(pt1.x() - 2.0, pt1.y()))
81274
path.lineTo(rect.center())
82275
painter.drawPath(path)
83276

@@ -124,6 +317,15 @@ def _paint_horizontal(self, painter, option, widget):
124317

125318
def _paint_vertical(self, painter, option, widget):
126319
painter.save()
320+
321+
# display node area for debugging
322+
# ----------------------------------------------------------------------
323+
# pen = QtGui.QPen(QtGui.QColor(255, 255, 255, 80), 0.8)
324+
# pen.setStyle(QtCore.Qt.DotLine)
325+
# painter.setPen(pen)
326+
# painter.drawRect(self.boundingRect())
327+
# ----------------------------------------------------------------------
328+
127329
rect = self.boundingRect()
128330
width = min(rect.width(), rect.height()) / 1.8
129331
rect = QtCore.QRectF(
@@ -202,6 +404,45 @@ def _align_icon_vertical(self, h_offset, v_offset):
202404
y = rect.center().y() - (icon_rect.height() / 2)
203405
self._icon_item.setPos(x + h_offset, y + v_offset)
204406

407+
def _align_widgets_horizontal(self, v_offset):
408+
if not self._widgets:
409+
return
410+
rect = self.boundingRect()
411+
y = rect.bottom() + v_offset
412+
inputs = [p for p in self.inputs if p.isVisible()]
413+
outputs = [p for p in self.outputs if p.isVisible()]
414+
for widget in self._widgets.values():
415+
widget_rect = widget.boundingRect()
416+
if not inputs:
417+
x = rect.left() + 10
418+
widget.widget().setTitleAlign('left')
419+
elif not outputs:
420+
x = rect.right() - widget_rect.width() - 10
421+
widget.widget().setTitleAlign('right')
422+
else:
423+
x = rect.center().x() - (widget_rect.width() / 2)
424+
widget.widget().setTitleAlign('center')
425+
widget.setPos(x, y)
426+
y += widget_rect.height()
427+
428+
def _align_widgets_vertical(self, v_offset):
429+
if not self._widgets:
430+
return
431+
rect = self.boundingRect()
432+
y = rect.center().y() + v_offset
433+
widget_height = 0.0
434+
for widget in self._widgets.values():
435+
widget_rect = widget.boundingRect()
436+
widget_height += widget_rect.height()
437+
y -= widget_height / 2
438+
439+
for widget in self._widgets.values():
440+
widget_rect = widget.boundingRect()
441+
x = rect.center().x() - (widget_rect.width() / 2)
442+
widget.widget().setTitleAlign('center')
443+
widget.setPos(x, y)
444+
y += widget_rect.height()
445+
205446
def _align_label_horizontal(self, h_offset, v_offset):
206447
rect = self.boundingRect()
207448
text_rect = self._text_item.boundingRect()
@@ -212,19 +453,30 @@ def _align_label_horizontal(self, h_offset, v_offset):
212453
def _align_label_vertical(self, h_offset, v_offset):
213454
rect = self.boundingRect()
214455
text_rect = self._text_item.boundingRect()
215-
x = rect.right() - (rect.width() / 3)
456+
x = rect.right() - (rect.width() / 4)
216457
y = rect.center().y() - (text_rect.height() / 2)
217458
self._text_item.setPos(x + h_offset, y + v_offset)
218459

219460
def _draw_node_horizontal(self):
220-
add_width = self.text_item.boundingRect().width() * 1.5
221-
add_height = self.text_item.boundingRect().width() / 2
222-
223461
# update port text items in visibility.
462+
text_width = 0
463+
port_widths = 0
224464
for port, text in self._input_items.items():
225465
text.setVisible(port.display_name)
466+
if port.display_name:
467+
if text.boundingRect().width() > text_width:
468+
text_width = text.boundingRect().width()
469+
port_widths += port.boundingRect().width() / len(self._input_items)
470+
226471
for port, text in self._output_items.items():
227472
text.setVisible(port.display_name)
473+
if port.display_name:
474+
if text.boundingRect().width() > text_width:
475+
text_width = text.boundingRect().width()
476+
port_widths += port.boundingRect().width() / len(self._output_items)
477+
478+
add_width = (text_width * 2) + port_widths
479+
add_height = self.text_item.boundingRect().width() / 2
228480

229481
# setup initial base size.
230482
self._set_base_size(add_w=add_width, add_h=add_height)
@@ -241,21 +493,28 @@ def _draw_node_horizontal(self):
241493
# arrange icon
242494
self.align_icon()
243495
# arrange input and output ports.
244-
self.align_ports(v_offset=add_height / 2)
496+
self.align_ports()
245497
# arrange node widgets
246498
self.align_widgets(v_offset=0.0)
247499

248500
self.update()
249501

250502
def _draw_node_vertical(self):
503+
add_height = 0
504+
251505
# hide the port text items in vertical layout.
252506
for port, text in self._input_items.items():
253507
text.setVisible(False)
508+
add_height += port.boundingRect().height() / 2
254509
for port, text in self._output_items.items():
255510
text.setVisible(False)
511+
add_height += port.boundingRect().height() / 2
512+
513+
if add_height < 50:
514+
add_height = 50
256515

257516
# setup initial base size.
258-
self._set_base_size(add_w=50, add_h=50)
517+
self._set_base_size(add_w=50, add_h=add_height)
259518
# set text color when node is initialized.
260519
self._set_text_color(self.text_color)
261520
# set the tooltip

docs/_images/node_circle.png

-6.39 KB
Loading

docs/_images/screenshot.png

-211 KB
Loading

docs/_images/vertical_layout.png

143 KB
Loading

examples/nodes/basic_nodes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ def __init__(self):
7171
self.add_input('in 2')
7272
self.add_input('in 3', multi_input=True)
7373
self.add_input('in 4', display_name=False)
74+
self.add_input('in 5', display_name=False)
7475

7576
# create node outputs
76-
self.add_output('out 1', display_name=False)
77+
self.add_output('out 1')
7778
self.add_output('out 2', multi_output=False)
78-
self.add_output('out 3', multi_output=True)
79+
self.add_output('out 3', multi_output=True, display_name=False)
80+
self.add_output('out 4', multi_output=True, display_name=False)

0 commit comments

Comments
 (0)