Skip to content

Commit 9002c0e

Browse files
committed
Add vertical layout
1 parent eace31d commit 9002c0e

File tree

5 files changed

+327
-50
lines changed

5 files changed

+327
-50
lines changed

NodeGraphQt/base/node.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
NODE_PROP_FILE,
1111
NODE_PROP_FLOAT,
1212
NODE_PROP_INT,
13-
IN_PORT, OUT_PORT)
13+
IN_PORT, OUT_PORT,
14+
NODE_LAYOUT_VERTICAL,
15+
NODE_LAYOUT_HORIZONTAL,
16+
NODE_LAYOUT_DIRECTION)
1417
from ..errors import PortRegistrationError
1518
from ..qgraphics.node_backdrop import BackdropNodeItem
16-
from ..qgraphics.node_base import NodeItem
19+
from ..qgraphics.node_base import NodeItem, NodeItemVertical
1720
from ..widgets.node_widgets import (NodeComboBox,
1821
NodeLineEdit,
1922
NodeFloatEdit,
@@ -514,7 +517,12 @@ def __init__(self):
514517
NODE_NAME = 'Base Node'
515518

516519
def __init__(self):
517-
super(BaseNode, self).__init__(NodeItem())
520+
view = None
521+
if NODE_LAYOUT_DIRECTION is NODE_LAYOUT_VERTICAL:
522+
view = NodeItemVertical()
523+
elif NODE_LAYOUT_DIRECTION is NODE_LAYOUT_HORIZONTAL:
524+
view = NodeItem()
525+
super(BaseNode, self).__init__(view)
518526
self._inputs = []
519527
self._outputs = []
520528
self._has_draw = False

NodeGraphQt/constants.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,16 @@
112112
Z_VAL_PORT = 2
113113
Z_VAL_NODE_WIDGET = 3
114114

115-
116115
# === ITEM CACHE MODE ===
117116

118117
# QGraphicsItem.NoCache
119118
# QGraphicsItem.DeviceCoordinateCache
120119
# QGraphicsItem.ItemCoordinateCache
121120

122-
ITEM_CACHE_MODE = QtWidgets.QGraphicsItem.DeviceCoordinateCache
121+
ITEM_CACHE_MODE = QtWidgets.QGraphicsItem.DeviceCoordinateCache
122+
123+
# === NODE LAYOUT DIRECTION ===
124+
125+
NODE_LAYOUT_VERTICAL = 0
126+
NODE_LAYOUT_HORIZONTAL = 1
127+
NODE_LAYOUT_DIRECTION = NODE_LAYOUT_HORIZONTAL

NodeGraphQt/qgraphics/node_base.py

Lines changed: 215 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def __init__(self, name='node', parent=None):
134134
self._output_items = {}
135135
self._widgets = {}
136136
self._proxy_mode = False
137-
self._porxy_mode_threshold = 70
137+
self._proxy_mode_threshold = 70
138138

139139
def paint(self, painter, option, widget):
140140
"""
@@ -221,7 +221,7 @@ def itemChange(self, change, value):
221221
if change == self.ItemSelectedChange and self.scene():
222222
self.reset_pipes()
223223
if value:
224-
self.hightlight_pipes()
224+
self.highlight_pipes()
225225
self.setZValue(Z_VAL_NODE)
226226
if not self.selected:
227227
self.setZValue(Z_VAL_NODE + 1)
@@ -274,7 +274,7 @@ def activate_pipes(self):
274274
for pipe in port.connected_pipes:
275275
pipe.activate()
276276

277-
def hightlight_pipes(self):
277+
def highlight_pipes(self):
278278
"""
279279
highlight pipe color.
280280
"""
@@ -497,7 +497,7 @@ def auto_switch_mode(self):
497497
# with is the node with in screen
498498
width = r.x() - l.x()
499499

500-
self.set_proxy_mode(width < self._porxy_mode_threshold)
500+
self.set_proxy_mode(width < self._proxy_mode_threshold)
501501

502502
def set_proxy_mode(self, mode):
503503
if mode is self._proxy_mode:
@@ -511,15 +511,13 @@ def set_proxy_mode(self, mode):
511511
w.widget.setVisible(visible)
512512
for port, text in self._input_items.items():
513513
port.setVisible(visible)
514-
text.setVisible(visible)
515-
# for pipe in port.connected_pipes:
516-
# pipe.setVisible(visible)
514+
if text.visible_:
515+
text.setVisible(visible)
517516

518517
for port, text in self._output_items.items():
519518
port.setVisible(visible)
520-
text.setVisible(visible)
521-
# for pipe in port.connected_pipes:
522-
# pipe.setVisible(visible)
519+
if text.visible_:
520+
text.setVisible(visible)
523521

524522
self.text_item.setVisible(visible)
525523
self._icon_item.setVisible(visible)
@@ -567,7 +565,7 @@ def disabled(self, state=False):
567565
def selected(self, selected=False):
568566
AbstractNodeItem.selected.fset(self, selected)
569567
if selected:
570-
self.hightlight_pipes()
568+
self.highlight_pipes()
571569

572570
@AbstractNodeItem.name.setter
573571
def name(self, name=''):
@@ -625,6 +623,7 @@ def add_input(self, name='input', multi_port=False, display_name=True):
625623
text.font().setPointSize(8)
626624
text.setFont(text.font())
627625
text.setVisible(display_name)
626+
text.visible_ = display_name
628627
text.setCacheMode(ITEM_CACHE_MODE)
629628
self._input_items[port] = text
630629
if self.scene():
@@ -650,6 +649,7 @@ def add_output(self, name='output', multi_port=False, display_name=True):
650649
text.font().setPointSize(8)
651650
text.setFont(text.font())
652651
text.setVisible(display_name)
652+
text.visible_ = display_name
653653
text.setCacheMode(ITEM_CACHE_MODE)
654654
self._output_items[port] = text
655655
if self.scene():
@@ -731,3 +731,207 @@ def from_dict(self, node_dict):
731731
for name, value in widgets.items():
732732
if self._widgets.get(name):
733733
self._widgets[name].value = value
734+
735+
736+
class NodeItemVertical(NodeItem):
737+
def __init__(self, name='node', parent=None):
738+
super(NodeItemVertical, self).__init__(name, parent)
739+
font = QtGui.QFont()
740+
font.setPointSize(15)
741+
self.text_item.setFont(font)
742+
743+
def paint(self, painter, option, widget):
744+
"""
745+
Draws the node base not the ports.
746+
747+
Args:
748+
painter (QtGui.QPainter): painter used for drawing the item.
749+
option (QtGui.QStyleOptionGraphicsItem):
750+
used to describe the parameters needed to draw.
751+
widget (QtWidgets.QWidget): not used.
752+
"""
753+
self.auto_switch_mode()
754+
755+
painter.save()
756+
bg_border = 1.0
757+
rect = QtCore.QRectF(0.5 - (bg_border / 2),
758+
0.5 - (bg_border / 2),
759+
self._width + bg_border,
760+
self._height + bg_border)
761+
radius = 2
762+
border_color = QtGui.QColor(*self.border_color)
763+
764+
path = QtGui.QPainterPath()
765+
path.addRoundedRect(rect, radius, radius)
766+
767+
rect = self.boundingRect()
768+
769+
bg_color = QtGui.QColor(*self.color)
770+
painter.setBrush(bg_color)
771+
painter.setPen(QtCore.Qt.NoPen)
772+
painter.drawRoundedRect(rect, radius, radius)
773+
774+
if self.selected and NODE_SEL_COLOR:
775+
painter.setBrush(QtGui.QColor(*NODE_SEL_COLOR))
776+
painter.drawRoundedRect(rect, radius, radius)
777+
778+
label_rect = QtCore.QRectF(rect.left(), rect.top(), self._width, 15)
779+
path = QtGui.QPainterPath()
780+
path.addRoundedRect(label_rect, radius, radius)
781+
painter.setBrush(QtGui.QColor(30, 30, 30, 200))
782+
painter.fillPath(path, painter.brush())
783+
784+
label_rect = QtCore.QRectF(rect.left(), rect.bottom()-15, self._width, 15)
785+
path = QtGui.QPainterPath()
786+
path.addRoundedRect(label_rect, radius, radius)
787+
painter.fillPath(path, painter.brush())
788+
789+
border_width = 0.8
790+
if self.selected and NODE_SEL_BORDER_COLOR:
791+
border_width = 1.2
792+
border_color = QtGui.QColor(*NODE_SEL_BORDER_COLOR)
793+
border_rect = QtCore.QRectF(rect.left() - (border_width / 2),
794+
rect.top() - (border_width / 2),
795+
rect.width() + border_width,
796+
rect.height() + border_width)
797+
798+
pen = QtGui.QPen(border_color, border_width)
799+
pen.setCosmetic(self.viewer().get_zoom() < 0.0)
800+
path = QtGui.QPainterPath()
801+
path.addRoundedRect(border_rect, radius, radius)
802+
painter.setBrush(QtCore.Qt.NoBrush)
803+
painter.setPen(pen)
804+
painter.drawPath(path)
805+
806+
painter.restore()
807+
808+
def arrange_icon(self, h_offset=0.0, v_offset=0.0):
809+
"""
810+
Arrange node icon to the default top left of the node.
811+
812+
Args:
813+
v_offset (float): vertical offset.
814+
h_offset (float): horizontal offset.
815+
"""
816+
# icon_rect = self._icon_item.boundingRect()
817+
# x = self._width / 2 - (icon_rect.width() / 2) + h_offset
818+
# y = self._height / 2 - (icon_rect.height() / 2) + v_offset
819+
x = 2.0 + h_offset
820+
y = 17.0 + v_offset
821+
self._icon_item.setPos(x, y)
822+
823+
def arrange_label(self, h_offset=0.0, v_offset=0.0):
824+
"""
825+
Arrange node label to the default top center of the node.
826+
827+
Args:
828+
v_offset (float): vertical offset.
829+
h_offset (float): horizontal offset.
830+
"""
831+
text_rect = self.text_item.boundingRect()
832+
text_x = self._width + 10 + h_offset
833+
text_y = self._height / 2 - (text_rect.height() / 2)
834+
self.text_item.setPos(text_x, text_y)
835+
836+
def arrange_ports(self, v_offset=0.0):
837+
"""
838+
Arrange input, output ports in the node layout.
839+
"""
840+
# adjust input position
841+
inputs = [p for p in self.inputs if p.isVisible()]
842+
if inputs:
843+
port_width = inputs[0].boundingRect().width()
844+
port_height = inputs[0].boundingRect().height()
845+
half_width = port_width/2
846+
delta = self._width / (len(inputs)+1)
847+
port_x = delta
848+
port_y = (port_height / 2) * -1
849+
for port in inputs:
850+
port.setPos(port_x - half_width, port_y)
851+
port_x += delta
852+
853+
# adjust output position
854+
outputs = [p for p in self.outputs if p.isVisible()]
855+
if outputs:
856+
port_width = outputs[0].boundingRect().width()
857+
port_height = outputs[0].boundingRect().height()
858+
half_width = port_width / 2
859+
delta = self._width / (len(outputs)+1)
860+
port_x = delta
861+
port_y = self._height - (port_height / 2)
862+
for port in outputs:
863+
port.setPos(port_x-half_width, port_y)
864+
port_x += delta
865+
866+
def draw_node(self):
867+
"""
868+
Draw the node item in the scene.
869+
"""
870+
# setup initial base size.
871+
self._set_base_size(add_w=0.0, add_h=0.0)
872+
# set text color when node is initialized.
873+
self._set_text_color(self.text_color)
874+
# set the tooltip
875+
self._tooltip_disable(self.disabled)
876+
877+
# --- setup node layout ---
878+
879+
# arrange label text
880+
self.arrange_label(h_offset=0.0, v_offset=0.0)
881+
# arrange icon
882+
self.arrange_icon(h_offset=0.0, v_offset=0.0)
883+
# arrange input and output ports.
884+
self.arrange_ports()
885+
# arrange node widgets
886+
self.arrange_widgets(v_offset=0.0)
887+
self.update()
888+
889+
def calc_size(self, add_w=0.0, add_h=0.0):
890+
"""
891+
calculate minimum node size.
892+
893+
Args:
894+
add_w (float): additional width.
895+
add_h (float): additional height.
896+
"""
897+
width = 0
898+
height = 0
899+
900+
if self._widgets:
901+
wid_width = max([
902+
w.boundingRect().width() for w in self._widgets.values()
903+
])
904+
width = max(width, wid_width)
905+
906+
port_width = 0.0
907+
if self._input_items:
908+
port = list(self._input_items.keys())[0]
909+
height += port.boundingRect().height()
910+
port_width = port.boundingRect().width()
911+
912+
if self._output_items:
913+
port = list(self._output_items.keys())[0]
914+
height += port.boundingRect().height()
915+
port_width = port.boundingRect().width()
916+
917+
in_count = len([p for p in self.inputs if p.isVisible()])
918+
out_count = len([p for p in self.outputs if p.isVisible()])
919+
width = max(width, port_width * max(in_count, out_count))
920+
if self._widgets:
921+
wid_height = 0.0
922+
for w in self._widgets.values():
923+
wid_height += w.boundingRect().height()
924+
wid_height += wid_height / len(self._widgets.values())
925+
if wid_height > height:
926+
height = wid_height
927+
928+
width += add_w
929+
height += add_h + 15
930+
931+
return width, height
932+
933+
def add_input(self, name='input', multi_port=False, display_name=False):
934+
return super(NodeItemVertical, self).add_input(name, multi_port, False)
935+
936+
def add_output(self, name='output', multi_port=False, display_name=False):
937+
return super(NodeItemVertical, self).add_output(name, multi_port, False)

0 commit comments

Comments
 (0)