From d1bf60386f4d41902a8a8159b2aa028cee4fa4b4 Mon Sep 17 00:00:00 2001 From: Jonas Sorgenfrei Date: Mon, 1 Sep 2025 00:59:33 +0200 Subject: [PATCH 1/2] update example with vertical aligned node --- examples/basic_example.py | 45 ++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/examples/basic_example.py b/examples/basic_example.py index dc01d936..7c26507a 100644 --- a/examples/basic_example.py +++ b/examples/basic_example.py @@ -5,18 +5,19 @@ from Qt import QtCore, QtWidgets +# import example nodes from the "nodes" sub-package +from examples.nodes import basic_nodes, custom_ports_node, group_node, widget_nodes from NodeGraphQt import ( NodeGraph, - PropertiesBinWidget, + NodesPaletteWidget, NodesTreeWidget, - NodesPaletteWidget + PropertiesBinWidget, ) - -# import example nodes from the "nodes" sub-package -from examples.nodes import basic_nodes, custom_ports_node, group_node, widget_nodes +from NodeGraphQt.constants import LayoutDirectionEnum BASE_PATH = Path(__file__).parent.resolve() + def main(): # handle SIGINT to make the app terminate on CTRL+C signal.signal(signal.SIGINT, signal.SIG_DFL) @@ -31,20 +32,23 @@ def main(): graph.set_context_menu_from_file(hotkey_path, 'graph') # registered example nodes. - graph.register_nodes([ - basic_nodes.BasicNodeA, - basic_nodes.BasicNodeB, - basic_nodes.CircleNode, - custom_ports_node.CustomPortsNode, - group_node.MyGroupNode, - widget_nodes.DropdownMenuNode, - widget_nodes.TextInputNode, - widget_nodes.CheckboxNode - ]) + graph.register_nodes( + [ + basic_nodes.BasicNodeA, + basic_nodes.BasicNodeB, + basic_nodes.CircleNode, + custom_ports_node.CustomPortsNode, + group_node.MyGroupNode, + widget_nodes.DropdownMenuNode, + widget_nodes.TextInputNode, + widget_nodes.CheckboxNode, + ] + ) # show the node graph widget. graph_widget = graph.widget graph_widget.resize(1100, 800) + graph_widget.setWindowTitle("NodeGraphQt Example") graph_widget.show() # create node with custom text color and disable it. @@ -52,6 +56,14 @@ def main(): 'nodes.basic.BasicNodeA', text_color='#feab20') n_basic_a.set_disabled(True) + # create node with vertial alignment + n_basic_a_vertical = graph.create_node( + "nodes.basic.BasicNodeA", name="Vertical Node", text_color="#feab20" + ) + + # adjust layout of node to be vertical + n_basic_a_vertical.set_layout_direction(1) + # create node and set a custom icon. n_basic_b = graph.create_node( 'nodes.basic.BasicNodeB', name='custom icon') @@ -106,6 +118,9 @@ def main(): graph.clear_selection() graph.fit_to_selection() + # adjust layout of node to be vertical (for all nodes). + # graph.set_layout_direction(LayoutDirectionEnum.VERTICAL.value) + # Custom builtin widgets from NodeGraphQt # --------------------------------------- From 5233b21c4454d0d99bc68f1727bd732cf2c015df Mon Sep 17 00:00:00 2001 From: Jonas Sorgenfrei Date: Mon, 1 Sep 2025 23:42:20 +0200 Subject: [PATCH 2/2] graph expose option to paste nodes without overwriting the style of the graph --- NodeGraphQt/base/graph.py | 62 ++++++++++++++-------------- examples/hotkeys/hotkey_functions.py | 5 ++- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/NodeGraphQt/base/graph.py b/NodeGraphQt/base/graph.py index 0bfeadc5..fc1e7c6b 100644 --- a/NodeGraphQt/base/graph.py +++ b/NodeGraphQt/base/graph.py @@ -8,24 +8,16 @@ from Qt import QtCore, QtWidgets -from NodeGraphQt.base.commands import (NodeAddedCmd, - NodesRemovedCmd, - NodeMovedCmd, - PortConnectedCmd) +from NodeGraphQt.base.commands import (NodeAddedCmd, NodeMovedCmd, + NodesRemovedCmd, PortConnectedCmd) from NodeGraphQt.base.factory import NodeFactory from NodeGraphQt.base.menu import NodeGraphMenu, NodesMenu from NodeGraphQt.base.model import NodeGraphModel from NodeGraphQt.base.node import NodeObject from NodeGraphQt.base.port import Port -from NodeGraphQt.constants import ( - MIME_TYPE, - URI_SCHEME, - URN_SCHEME, - LayoutDirectionEnum, - PipeLayoutEnum, - PortTypeEnum, - ViewerEnum -) +from NodeGraphQt.constants import (MIME_TYPE, URI_SCHEME, URN_SCHEME, + LayoutDirectionEnum, PipeLayoutEnum, + PortTypeEnum, ViewerEnum) from NodeGraphQt.errors import NodeCreationError, NodeDeletionError from NodeGraphQt.nodes.backdrop_node import BackdropNode from NodeGraphQt.nodes.base_node import BaseNode @@ -789,8 +781,8 @@ def _deserialize_context_menu(self, menu, menu_data, anchor_path=None): if not menu: raise ValueError('No context menu named: "{}"'.format(menu)) - import sys import importlib.util + import sys nodes_menu = self.get_context_menu('nodes') @@ -1304,7 +1296,7 @@ def format_color(clr): raise NodeCreationError('Can\'t find node: "{}"'.format(node_type)) - def add_node(self, node, pos=None, selected=True, push_undo=True): + def add_node(self, node, pos=None, selected=True, push_undo=True, inherite_graph_style=True): """ Add a node into the node graph. unlike the :meth:`NodeGraph.create_node` function this will not @@ -1315,6 +1307,8 @@ def add_node(self, node, pos=None, selected=True, push_undo=True): pos (list[float]): node x,y position. (optional) selected (bool): node selected state. (optional) push_undo (bool): register the command to the undo stack. (default: True) + inherite_graph_style (bool): when True the node will inherite the + node graph layout direction. (default: True) """ assert isinstance(node, NodeObject), 'node must be a Node instance.' @@ -1368,7 +1362,8 @@ def add_node(self, node, pos=None, selected=True, push_undo=True): node.model.name = node.NODE_NAME # initial node direction layout. - node.model.layout_direction = self.layout_direction() + if inherite_graph_style: + node.model.layout_direction = self.layout_direction() # update method must be called before it's been added to the viewer. node.update() @@ -1774,7 +1769,7 @@ def _serialize(self, nodes): return serial_data - def _deserialize(self, data, relative_pos=False, pos=None): + def _deserialize(self, data, relative_pos=False, pos=None, adjust_graph_style=True): """ deserialize node data. (used internally by the node graph) @@ -1783,22 +1778,24 @@ def _deserialize(self, data, relative_pos=False, pos=None): data (dict): node data. relative_pos (bool): position node relative to the cursor. pos (tuple or list): custom x, y position. + adjust_graph_style (bool): if true adjust the node graph properties Returns: list[NodeGraphQt.Nodes]: list of node instances. """ # update node graph properties. - for attr_name, attr_value in data.get('graph', {}).items(): - if attr_name == 'layout_direction': - self.set_layout_direction(attr_value) - elif attr_name == 'acyclic': - self.set_acyclic(attr_value) - elif attr_name == 'pipe_collision': - self.set_pipe_collision(attr_value) - elif attr_name == 'pipe_slicing': - self.set_pipe_slicing(attr_value) - elif attr_name == 'pipe_style': - self.set_pipe_style(attr_value) + for attr_name, attr_value in data.get("graph", {}).items(): + if adjust_graph_style: + if attr_name == "layout_direction": + self.set_layout_direction(attr_value) + elif attr_name == "acyclic": + self.set_acyclic(attr_value) + elif attr_name == "pipe_collision": + self.set_pipe_collision(attr_value) + elif attr_name == "pipe_slicing": + self.set_pipe_slicing(attr_value) + elif attr_name == "pipe_style": + self.set_pipe_style(attr_value) # connection constrains. elif attr_name == 'accept_connection_types': @@ -1829,7 +1826,7 @@ def _deserialize(self, data, relative_pos=False, pos=None): node.view.widgets[prop].set_value(val) nodes[n_id] = node - self.add_node(node, n_data.get('pos')) + self.add_node(node, n_data.get('pos'), inherite_graph_style=adjust_graph_style) if n_data.get('port_deletion_allowed', None): node.set_ports({ @@ -2057,10 +2054,13 @@ def cut_nodes(self, nodes=None): self._undo_stack.push(NodesRemovedCmd(self, nodes)) self._undo_stack.endMacro() - def paste_nodes(self): + def paste_nodes(self, adjust_graph_style=True): """ Pastes nodes copied from the clipboard. + Args: + adjust_graph_style (bool): if true adjust the node graph properties + other wise only the nodes are pasted. Returns: list[NodeGraphQt.BaseNode]: list of pasted node instances. """ @@ -2078,7 +2078,7 @@ def paste_nodes(self): self._undo_stack.beginMacro('pasted nodes') self.clear_selection() - nodes = self._deserialize(serial_data, relative_pos=True) + nodes = self._deserialize(serial_data, relative_pos=True, adjust_graph_style=adjust_graph_style) [n.set_selected(True) for n in nodes] self._undo_stack.endMacro() return nodes diff --git a/examples/hotkeys/hotkey_functions.py b/examples/hotkeys/hotkey_functions.py index 266857ef..4c74c68d 100644 --- a/examples/hotkeys/hotkey_functions.py +++ b/examples/hotkeys/hotkey_functions.py @@ -128,7 +128,10 @@ def paste_nodes(graph): """ Pastes nodes copied from the clipboard. """ - graph.paste_nodes() + # by default the graph will inherite the global style + # from the graph when pasting nodes. + # to disable this behaviour set `adjust_graph_style` to False. + graph.paste_nodes(adjust_graph_style=False) def delete_nodes(graph):