Skip to content

Commit 548d8a3

Browse files
committed
Refactored NodeItem drawing logic addressing issues #240,#221
1 parent 276d4a1 commit 548d8a3

File tree

6 files changed

+162
-149
lines changed

6 files changed

+162
-149
lines changed

NodeGraphQt/qgraphics/node_base.py

Lines changed: 93 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,17 @@ def _tooltip_disable(self, state):
194194

195195
def _set_base_size(self, add_w=0.0, add_h=0.0):
196196
"""
197-
setup initial base size.
197+
Sets the initial base size for the node.
198198
199199
Args:
200-
add_w (float): additional width.
201-
add_h (float): additional height.
200+
add_w (float): add additional width.
201+
add_h (float): add additional height.
202202
"""
203-
width, height = self.calc_size(add_w, add_h)
204-
self._width = width if width > NODE_WIDTH else NODE_WIDTH
205-
self._height = height if height > NODE_HEIGHT else NODE_HEIGHT
203+
self._width, self._height = self.calc_size(add_w, add_h)
204+
if self._width < NODE_WIDTH:
205+
self._width = NODE_WIDTH
206+
if self._height < NODE_HEIGHT:
207+
self._height = NODE_HEIGHT
206208

207209
def _set_text_color(self, color):
208210
"""
@@ -256,52 +258,65 @@ def calc_size(self, add_w=0.0, add_h=0.0):
256258
Returns:
257259
tuple(float, float): width, height.
258260
"""
259-
width = self._text_item.boundingRect().width()
260-
height = self._text_item.boundingRect().height()
261-
262-
if self._widgets:
263-
wid_width = max([
264-
w.boundingRect().width() for w in self._widgets.values()
265-
])
266-
if width < wid_width:
267-
width = wid_width
268-
269-
port_height = 0.0
270-
if self._input_items:
271-
port = None
272-
input_widths = []
273-
for port, text in self._input_items.items():
274-
input_width = port.boundingRect().width() - PORT_FALLOFF
275-
if text.isVisible():
276-
input_width += text.boundingRect().width() / 1.5
277-
input_widths.append(input_width)
278-
width += max(input_widths)
279-
if port:
280-
port_height = port.boundingRect().height()
281-
282-
if self._output_items:
283-
port = None
284-
output_widths = []
285-
for port, text in self._output_items.items():
286-
output_width = port.boundingRect().width()
287-
if text.isVisible():
288-
output_width += text.boundingRect().width() / 1.5
289-
output_widths.append(output_width)
290-
width += max(output_widths)
291-
if port:
292-
port_height = port.boundingRect().height()
293-
294-
in_count = len([p for p in self.inputs if p.isVisible()])
295-
out_count = len([p for p in self.outputs if p.isVisible()])
296-
height += port_height * max([in_count, out_count])
297-
if self._widgets:
298-
wid_height = 0.0
299-
for w in self._widgets.values():
300-
wid_height += w.boundingRect().height()
301-
wid_height += wid_height / len(self._widgets.values())
302-
if wid_height > height:
303-
height = wid_height
261+
# width, height from node name text.
262+
text_w = self._text_item.boundingRect().width()
263+
text_h = self._text_item.boundingRect().height()
304264

265+
# width, height from node ports.
266+
port_width = 0.0
267+
p_input_text_width = 0.0
268+
p_output_text_width = 0.0
269+
p_input_height = 0.0
270+
p_output_height = 0.0
271+
for port, text in self._input_items.items():
272+
if not port.isVisible():
273+
continue
274+
if not port_width:
275+
port_width = port.boundingRect().width()
276+
t_width = text.boundingRect().width()
277+
if text.isVisible() and t_width > p_input_text_width:
278+
p_input_text_width = text.boundingRect().width()
279+
p_input_height += port.boundingRect().height()
280+
for port, text in self._output_items.items():
281+
if not port.isVisible():
282+
continue
283+
if not port_width:
284+
port_width = port.boundingRect().width()
285+
t_width = text.boundingRect().width()
286+
if text.isVisible() and t_width > p_output_text_width:
287+
p_output_text_width = text.boundingRect().width()
288+
p_output_height += port.boundingRect().height()
289+
290+
port_text_width = p_input_text_width + p_output_text_width
291+
292+
# width, height from node embedded widgets.
293+
widget_width = 0.0
294+
widget_height = 0.0
295+
for widget in self._widgets.values():
296+
w_width = widget.boundingRect().width()
297+
w_height = widget.boundingRect().height()
298+
if w_width > widget_width:
299+
widget_width = w_width
300+
widget_height += w_height
301+
302+
side_padding = 0.0
303+
if all([widget_width, p_input_text_width, p_output_text_width]):
304+
port_text_width = max([p_input_text_width, p_output_text_width])
305+
port_text_width *= 2
306+
elif widget_width:
307+
side_padding = 10
308+
309+
width = port_width + max([text_w, port_text_width]) + side_padding
310+
height = max([text_h, p_input_height, p_output_height, widget_height])
311+
if widget_width:
312+
# add additional width for node widget.
313+
width += widget_width
314+
if widget_height:
315+
# add bottom margin for node widget.
316+
height += 4.0
317+
height *= 1.05
318+
319+
# additional width, height.
305320
width += add_w
306321
height += add_h
307322
return width, height
@@ -311,12 +326,12 @@ def align_icon(self, h_offset=0.0, v_offset=0.0):
311326
Align node icon to the default top left of the node.
312327
313328
Args:
314-
v_offset (float): vertical offset.
315-
h_offset (float): horizontal offset.
329+
v_offset (float): additional vertical offset.
330+
h_offset (float): additional horizontal offset.
316331
"""
317-
x = 2.0 + h_offset
318-
y = 1.0 + v_offset
319-
self._icon_item.setPos(x, y)
332+
top_left = self.boundingRect().topLeft()
333+
x, y = top_left.x(), top_left.y()
334+
self._icon_item.setPos(x + h_offset, y + v_offset)
320335

321336
def align_label(self, h_offset=0.0, v_offset=0.0):
322337
"""
@@ -329,8 +344,7 @@ def align_label(self, h_offset=0.0, v_offset=0.0):
329344
rect = self.boundingRect()
330345
text_rect = self._text_item.boundingRect()
331346
x = rect.center().x() - (text_rect.width() / 2)
332-
y = 0.0
333-
self._text_item.setPos(x + h_offset, y + v_offset)
347+
self._text_item.setPos(x + h_offset, rect.y() + v_offset)
334348

335349
def align_widgets(self, v_offset=0.0):
336350
"""
@@ -341,16 +355,23 @@ def align_widgets(self, v_offset=0.0):
341355
"""
342356
if not self._widgets:
343357
return
344-
wid_heights = sum(
345-
[w.boundingRect().height() for w in self._widgets.values()])
346-
pos_y = self._height / 2
347-
pos_y -= wid_heights / 2
348-
pos_y += v_offset
358+
rect = self.boundingRect()
359+
y = rect.y() + v_offset
360+
inputs = [p for p in self.inputs if p.isVisible()]
361+
outputs = [p for p in self.outputs if p.isVisible()]
349362
for widget in self._widgets.values():
350-
rect = widget.boundingRect()
351-
pos_x = (self._width / 2) - (rect.width() / 2)
352-
widget.setPos(pos_x, pos_y)
353-
pos_y += rect.height()
363+
widget_rect = widget.boundingRect()
364+
if not inputs:
365+
x = rect.left() + 10
366+
widget.widget().setTitleAlign('left')
367+
elif not outputs:
368+
x = rect.right() - widget_rect.width() - 10
369+
widget.widget().setTitleAlign('right')
370+
else:
371+
x = rect.center().x() - (widget_rect.width() / 2)
372+
widget.widget().setTitleAlign('center')
373+
widget.setPos(x, y)
374+
y += widget_rect.height()
354375

355376
def align_ports(self, v_offset=0.0):
356377
"""
@@ -401,10 +422,10 @@ def draw_node(self):
401422
Re-draw the node item in the scene.
402423
(re-implemented for vertical layout design)
403424
"""
404-
height = self._text_item.boundingRect().height()
425+
height = self._text_item.boundingRect().height() + 4.0
405426

406427
# setup initial base size.
407-
self._set_base_size(add_w=0.0, add_h=height)
428+
self._set_base_size(add_h=height)
408429
# set text color when node is initialized.
409430
self._set_text_color(self.text_color)
410431
# set the tooltip
@@ -416,11 +437,11 @@ def draw_node(self):
416437
# align label text
417438
self.align_label()
418439
# align icon
419-
self.align_icon()
440+
self.align_icon(h_offset=2.0, v_offset=1.0)
420441
# arrange input and output ports.
421-
self.align_ports(v_offset=height + (height / 2))
442+
self.align_ports(v_offset=height)
422443
# arrange node widgets
423-
self.align_widgets(v_offset=height / 2)
444+
self.align_widgets(v_offset=height)
424445

425446
self.update()
426447

NodeGraphQt/qgraphics/node_group.py

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -106,32 +106,6 @@ def paint(self, painter, option, widget):
106106

107107
painter.restore()
108108

109-
def align_icon(self, h_offset=0.0, v_offset=0.0):
110-
"""
111-
Align node icon to the default top left of the node.
112-
113-
Args:
114-
v_offset (float): vertical offset.
115-
h_offset (float): horizontal offset.
116-
"""
117-
x = 5.0 + h_offset
118-
y = 3.0 + v_offset
119-
self._icon_item.setPos(x, y)
120-
121-
def align_label(self, h_offset=0.0, v_offset=0.0):
122-
"""
123-
Center node label text to the top of the node.
124-
125-
Args:
126-
v_offset (float): vertical offset.
127-
h_offset (float): horizontal offset.
128-
"""
129-
rect = self.boundingRect()
130-
text_rect = self._text_item.boundingRect()
131-
x = rect.center().x() - (text_rect.width() / 2)
132-
y = 2.0
133-
self._text_item.setPos(x + h_offset, y + v_offset)
134-
135109
def align_ports(self, v_offset=0.0):
136110
"""
137111
Align input, output ports in the node layout.
@@ -187,7 +161,7 @@ def draw_node(self):
187161
height = self._text_item.boundingRect().height()
188162

189163
# setup initial base size.
190-
self._set_base_size(add_w=8.0, add_h=height + 10.0)
164+
self._set_base_size(add_w=8.0, add_h=height)
191165
# set text color when node is initialized.
192166
self._set_text_color(self.text_color)
193167
# set the tooltip
@@ -199,11 +173,11 @@ def draw_node(self):
199173
# align label text
200174
self.align_label()
201175
# arrange icon
202-
self.align_icon()
176+
self.align_icon(h_offset=2.0, v_offset=3.0)
203177
# arrange input and output ports.
204-
self.align_ports(v_offset=height + (height / 2))
178+
self.align_ports(v_offset=height)
205179
# arrange node widgets
206-
self.align_widgets(v_offset=height / 2)
180+
self.align_widgets(v_offset=height)
207181

208182
self.update()
209183

NodeGraphQt/qgraphics/node_text_item.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ def set_node_name(self, name):
8484
Args:
8585
name (str): new node name.
8686
"""
87-
if name == self.node.name:
88-
return
89-
viewer = self.node.viewer()
90-
viewer.node_name_changed.emit(self.node.id, name)
87+
name = name.strip()
88+
if name != self.node.name:
89+
viewer = self.node.viewer()
90+
viewer.node_name_changed.emit(self.node.id, name)
9191

9292
def set_locked(self, state=False):
9393
"""

NodeGraphQt/widgets/node_widgets.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,41 @@
11
#!/usr/bin/python
2+
import re
23
from Qt import QtCore, QtWidgets
34

45
from NodeGraphQt.constants import Z_VAL_NODE_WIDGET
56
from NodeGraphQt.errors import NodeWidgetError
6-
from NodeGraphQt.widgets.stylesheet import STYLE_QGROUPBOX, STYLE_QLINEEDIT
77

8+
REGEX = re.compile(r'((?: +)*([\w-]+):(?: )*<\$VALUE>;)')
9+
10+
STYLE_NODE_GROUPBOX = '''
11+
QGroupBox {
12+
background-color: rgba(0, 0, 0, 0);
13+
border: 0px solid rgba(0, 0, 0, 0);
14+
margin-top: 1px;
15+
padding-top: <$VALUE>;
16+
padding-bottom: 2px;
17+
padding-left: 1px;
18+
padding-right: 1px;
19+
font-size: 8pt;
20+
}
21+
QGroupBox::title {
22+
subcontrol-origin: margin;
23+
subcontrol-position: <$VALUE>;
24+
margin-left: <$VALUE>;
25+
margin-right: <$VALUE>;
26+
color: rgba(255, 255, 255, 85);
27+
padding: 0px;
28+
}
29+
'''
30+
STYLE_NODE_LINE_EDIT = '''
31+
QLineEdit {
32+
border: 1px solid rgb(90, 90, 90);
33+
border-radius: 0px;
34+
color: rgba(255, 255, 255, 150);
35+
background: rgba(0, 0, 0, 80);
36+
selection-background-color: rgba(255, 198, 10, 155);
37+
}
38+
'''
839

940
class _NodeGroupBox(QtWidgets.QGroupBox):
1041

@@ -15,16 +46,32 @@ def __init__(self, label, parent=None):
1546
self.setTitle(label)
1647

1748
def setTitle(self, text):
18-
margin = (0, 0, 0, 0)
19-
padding_top = '14px'
20-
if text == '':
21-
margin = (0, 2, 0, 0)
22-
padding_top = '2px'
23-
style = STYLE_QGROUPBOX.replace('$PADDING_TOP', padding_top)
49+
margin = (0, 2, 0, 0) if text else (0, 0, 0, 0)
2450
self.layout().setContentsMargins(*margin)
25-
self.setStyleSheet(style)
2651
super(_NodeGroupBox, self).setTitle(text)
2752

53+
def setTitleAlign(self, align='center'):
54+
style_param = {
55+
'padding-top': '14px' if self.title() else '2px',
56+
'subcontrol-position': 'top'
57+
}
58+
if align == 'center':
59+
style_param['subcontrol-position'] += ' center'
60+
elif align == 'left':
61+
style_param['subcontrol-position'] += ' left'
62+
style_param['margin-left'] = '4px'
63+
elif align == 'right':
64+
style_param['subcontrol-position'] += ' right'
65+
style_param['margin-right'] = '4px'
66+
style = STYLE_NODE_GROUPBOX
67+
for find_str, key in REGEX.findall(style):
68+
if key not in style_param:
69+
style = style.replace(find_str, '')
70+
continue
71+
replace_str = find_str.replace('<$VALUE>', style_param[key])
72+
style = style.replace(find_str, replace_str)
73+
self.setStyleSheet(style)
74+
2875
def add_node_widget(self, widget):
2976
self.layout().addWidget(widget)
3077

@@ -290,7 +337,7 @@ def __init__(self, parent=None, name='', label='', text=''):
290337
super(NodeLineEdit, self).__init__(parent, name, label)
291338
ledit = QtWidgets.QLineEdit()
292339
ledit.setText(text)
293-
ledit.setStyleSheet(STYLE_QLINEEDIT)
340+
ledit.setStyleSheet(STYLE_NODE_LINE_EDIT)
294341
ledit.setAlignment(QtCore.Qt.AlignCenter)
295342
ledit.editingFinished.connect(self.on_value_changed)
296343
ledit.clearFocus()

0 commit comments

Comments
 (0)