Migrated codebase to OdenGraphQT Successfully
This commit is contained in:
parent
2a95535e0a
commit
ca5152b89a
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
from NodeGraphQt import BaseNode
|
from OdenGraphQt import BaseNode
|
||||||
|
|
||||||
class ArrayNode(BaseNode):
|
class ArrayNode(BaseNode):
|
||||||
"""
|
"""
|
||||||
@ -11,8 +11,6 @@ class ArrayNode(BaseNode):
|
|||||||
__identifier__ = 'bunny-lab.io.array_node'
|
__identifier__ = 'bunny-lab.io.array_node'
|
||||||
NODE_NAME = 'Array'
|
NODE_NAME = 'Array'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(ArrayNode, self).__init__()
|
super(ArrayNode, self).__init__()
|
||||||
self.values = {} # Ensure values is a dictionary.
|
self.values = {} # Ensure values is a dictionary.
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Enhanced Backdrop Node (Inherited from BaseNode)
|
|
||||||
|
|
||||||
Features:
|
|
||||||
- Inherits from `BaseNode` so it can be discovered in your node scanning.
|
|
||||||
- Custom context menu to rename (set title) or pick a new color.
|
|
||||||
- Forces geometry updates to reduce "ghosting" or partial redraws.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from Qt import QtWidgets, QtGui
|
|
||||||
from NodeGraphQt import BaseNode
|
|
||||||
from NodeGraphQt.constants import NodePropWidgetEnum
|
|
||||||
from NodeGraphQt.qgraphics.node_backdrop import BackdropNodeItem
|
|
||||||
|
|
||||||
|
|
||||||
class BackdropNode(BaseNode):
|
|
||||||
"""
|
|
||||||
Backdrop Node:
|
|
||||||
- Allows grouping or annotating other nodes by resizing a large rectangle.
|
|
||||||
- Provides a custom context menu for renaming and recoloring (via on_context_menu).
|
|
||||||
"""
|
|
||||||
|
|
||||||
__identifier__ = 'bunny-lab.io.backdrop'
|
|
||||||
NODE_NAME = 'Backdrop'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
# Use BackdropNodeItem for the specialized QGraphicsItem.
|
|
||||||
super(BackdropNode, self).__init__(qgraphics_item=BackdropNodeItem)
|
|
||||||
|
|
||||||
# Default color (teal).
|
|
||||||
self.model.color = (5, 129, 138, 255)
|
|
||||||
|
|
||||||
# Multi-line text property for storing the backdrop text.
|
|
||||||
self.create_property(
|
|
||||||
'backdrop_text',
|
|
||||||
'',
|
|
||||||
widget_type=NodePropWidgetEnum.QTEXT_EDIT.value,
|
|
||||||
tab='Backdrop'
|
|
||||||
)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
|
||||||
# Resizing / Geometry
|
|
||||||
# --------------------------------------------------------------------------
|
|
||||||
def on_backdrop_updated(self, update_prop, value=None):
|
|
||||||
"""
|
|
||||||
Triggered when the user resizes or double-clicks the backdrop sizer handle.
|
|
||||||
"""
|
|
||||||
if not self.graph:
|
|
||||||
return
|
|
||||||
|
|
||||||
if update_prop == 'sizer_mouse_release':
|
|
||||||
# User finished dragging the resize handle
|
|
||||||
self.view.prepareGeometryChange()
|
|
||||||
self.graph.begin_undo(f'resized "{self.name()}"')
|
|
||||||
self.set_property('width', value['width'])
|
|
||||||
self.set_property('height', value['height'])
|
|
||||||
self.set_pos(*value['pos'])
|
|
||||||
self.graph.end_undo()
|
|
||||||
self.view.update()
|
|
||||||
|
|
||||||
elif update_prop == 'sizer_double_clicked':
|
|
||||||
# User double-clicked the resize handle (auto-resize)
|
|
||||||
self.view.prepareGeometryChange()
|
|
||||||
self.graph.begin_undo(f'"{self.name()}" auto resize')
|
|
||||||
self.set_property('width', value['width'])
|
|
||||||
self.set_property('height', value['height'])
|
|
||||||
self.set_pos(*value['pos'])
|
|
||||||
self.graph.end_undo()
|
|
||||||
self.view.update()
|
|
||||||
|
|
||||||
def auto_size(self):
|
|
||||||
"""
|
|
||||||
Auto-resize the backdrop to fit around intersecting nodes.
|
|
||||||
"""
|
|
||||||
if not self.graph:
|
|
||||||
return
|
|
||||||
self.view.prepareGeometryChange()
|
|
||||||
self.graph.begin_undo(f'"{self.name()}" auto resize')
|
|
||||||
size = self.view.calc_backdrop_size()
|
|
||||||
self.set_property('width', size['width'])
|
|
||||||
self.set_property('height', size['height'])
|
|
||||||
self.set_pos(*size['pos'])
|
|
||||||
self.graph.end_undo()
|
|
||||||
self.view.update()
|
|
||||||
|
|
||||||
def wrap_nodes(self, nodes):
|
|
||||||
"""
|
|
||||||
Fit the backdrop around the specified nodes.
|
|
||||||
"""
|
|
||||||
if not self.graph or not nodes:
|
|
||||||
return
|
|
||||||
self.view.prepareGeometryChange()
|
|
||||||
self.graph.begin_undo(f'"{self.name()}" wrap nodes')
|
|
||||||
size = self.view.calc_backdrop_size([n.view for n in nodes])
|
|
||||||
self.set_property('width', size['width'])
|
|
||||||
self.set_property('height', size['height'])
|
|
||||||
self.set_pos(*size['pos'])
|
|
||||||
self.graph.end_undo()
|
|
||||||
self.view.update()
|
|
||||||
|
|
||||||
def nodes(self):
|
|
||||||
"""
|
|
||||||
Return a list of nodes wrapped by this backdrop.
|
|
||||||
"""
|
|
||||||
node_ids = [n.id for n in self.view.get_nodes()]
|
|
||||||
return [self.graph.get_node_by_id(nid) for nid in node_ids]
|
|
||||||
|
|
||||||
def set_text(self, text=''):
|
|
||||||
"""
|
|
||||||
Set the multi-line text in the backdrop.
|
|
||||||
"""
|
|
||||||
self.set_property('backdrop_text', text)
|
|
||||||
|
|
||||||
def text(self):
|
|
||||||
"""
|
|
||||||
Return the text content in the backdrop.
|
|
||||||
"""
|
|
||||||
return self.get_property('backdrop_text')
|
|
||||||
|
|
||||||
def set_size(self, width, height):
|
|
||||||
"""
|
|
||||||
Manually set the backdrop size.
|
|
||||||
"""
|
|
||||||
if self.graph:
|
|
||||||
self.view.prepareGeometryChange()
|
|
||||||
self.graph.begin_undo('backdrop size')
|
|
||||||
self.set_property('width', width)
|
|
||||||
self.set_property('height', height)
|
|
||||||
self.graph.end_undo()
|
|
||||||
self.view.update()
|
|
||||||
else:
|
|
||||||
self.view.width, self.view.height = width, height
|
|
||||||
self.model.width, self.model.height = width, height
|
|
||||||
|
|
||||||
def size(self):
|
|
||||||
"""
|
|
||||||
Return (width, height) of the backdrop.
|
|
||||||
"""
|
|
||||||
self.model.width = self.view.width
|
|
||||||
self.model.height = self.view.height
|
|
||||||
return self.model.width, self.model.height
|
|
||||||
|
|
||||||
# No ports for a backdrop:
|
|
||||||
def inputs(self):
|
|
||||||
return
|
|
||||||
|
|
||||||
def outputs(self):
|
|
||||||
return
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
|
||||||
# Custom Context Menu
|
|
||||||
# --------------------------------------------------------------------------
|
|
||||||
def on_context_menu(self, menu):
|
|
||||||
"""
|
|
||||||
Called manually by the node context menu callback in older NodeGraphQt versions.
|
|
||||||
"""
|
|
||||||
rename_action = menu.addAction("Set Title...")
|
|
||||||
rename_action.triggered.connect(self._change_title)
|
|
||||||
|
|
||||||
color_action = menu.addAction("Set Color...")
|
|
||||||
color_action.triggered.connect(self._change_color)
|
|
||||||
|
|
||||||
def _change_title(self):
|
|
||||||
"""
|
|
||||||
Prompt for a new backdrop title (header).
|
|
||||||
"""
|
|
||||||
new_title, ok = QtWidgets.QInputDialog.getText(
|
|
||||||
None, "Backdrop Title", "Enter new backdrop title:"
|
|
||||||
)
|
|
||||||
if ok and new_title:
|
|
||||||
self.set_name(new_title)
|
|
||||||
|
|
||||||
def _change_color(self):
|
|
||||||
"""
|
|
||||||
Prompt for a new backdrop color via QColorDialog.
|
|
||||||
"""
|
|
||||||
current_color = QtGui.QColor(*self.model.color)
|
|
||||||
color = QtWidgets.QColorDialog.getColor(
|
|
||||||
current_color, None, "Select Backdrop Color"
|
|
||||||
)
|
|
||||||
if color.isValid():
|
|
||||||
self.model.color = (color.red(), color.green(), color.blue(), color.alpha())
|
|
||||||
self.view.update()
|
|
@ -1,46 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
from NodeGraphQt.nodes.base_node import BaseNode
|
|
||||||
from NodeGraphQt.qgraphics.node_circle import CircleNodeItem
|
|
||||||
|
|
||||||
|
|
||||||
class BaseNodeCircle(BaseNode):
|
|
||||||
"""
|
|
||||||
`Implemented in` ``v0.5.2``
|
|
||||||
|
|
||||||
The ``NodeGraphQt.BaseNodeCircle`` is pretty much the same class as the
|
|
||||||
:class:`NodeGraphQt.BaseNode` except with a different design.
|
|
||||||
|
|
||||||
.. inheritance-diagram:: NodeGraphQt.BaseNodeCircle
|
|
||||||
|
|
||||||
.. image:: ../_images/node_circle.png
|
|
||||||
:width: 250px
|
|
||||||
|
|
||||||
example snippet:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
:linenos:
|
|
||||||
|
|
||||||
from NodeGraphQt import BaseNodeCircle
|
|
||||||
|
|
||||||
class ExampleNode(BaseNodeCircle):
|
|
||||||
|
|
||||||
# unique node identifier domain.
|
|
||||||
__identifier__ = 'io.jchanvfx.github'
|
|
||||||
|
|
||||||
# initial default node name.
|
|
||||||
NODE_NAME = 'My Node'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(ExampleNode, self).__init__()
|
|
||||||
|
|
||||||
# create an input port.
|
|
||||||
self.add_input('in')
|
|
||||||
|
|
||||||
# create an output port.
|
|
||||||
self.add_output('out')
|
|
||||||
"""
|
|
||||||
|
|
||||||
NODE_NAME = 'Circle Node'
|
|
||||||
|
|
||||||
def __init__(self, qgraphics_item=None):
|
|
||||||
super(BaseNodeCircle, self).__init__(qgraphics_item or CircleNodeItem)
|
|
@ -1,876 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from NodeGraphQt.base.commands import NodeVisibleCmd, NodeWidgetVisibleCmd
|
|
||||||
from NodeGraphQt.base.node import NodeObject
|
|
||||||
from NodeGraphQt.base.port import Port
|
|
||||||
from NodeGraphQt.constants import NodePropWidgetEnum, PortTypeEnum
|
|
||||||
from NodeGraphQt.errors import (
|
|
||||||
PortError,
|
|
||||||
PortRegistrationError,
|
|
||||||
NodeWidgetError
|
|
||||||
)
|
|
||||||
from NodeGraphQt.qgraphics.node_base import NodeItem
|
|
||||||
from NodeGraphQt.widgets.node_widgets import (
|
|
||||||
NodeBaseWidget,
|
|
||||||
NodeCheckBox,
|
|
||||||
NodeComboBox,
|
|
||||||
NodeLineEdit
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseNode(NodeObject):
|
|
||||||
"""
|
|
||||||
The ``NodeGraphQt.BaseNode`` class is the base class for nodes that allows
|
|
||||||
port connections from one node to another.
|
|
||||||
|
|
||||||
.. inheritance-diagram:: NodeGraphQt.BaseNode
|
|
||||||
|
|
||||||
.. image:: ../_images/node.png
|
|
||||||
:width: 250px
|
|
||||||
|
|
||||||
example snippet:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
:linenos:
|
|
||||||
|
|
||||||
from NodeGraphQt import BaseNode
|
|
||||||
|
|
||||||
class ExampleNode(BaseNode):
|
|
||||||
|
|
||||||
# unique node identifier domain.
|
|
||||||
__identifier__ = 'io.jchanvfx.github'
|
|
||||||
|
|
||||||
# initial default node name.
|
|
||||||
NODE_NAME = 'My Node'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(ExampleNode, self).__init__()
|
|
||||||
|
|
||||||
# create an input port.
|
|
||||||
self.add_input('in')
|
|
||||||
|
|
||||||
# create an output port.
|
|
||||||
self.add_output('out')
|
|
||||||
"""
|
|
||||||
|
|
||||||
NODE_NAME = 'Node'
|
|
||||||
|
|
||||||
def __init__(self, qgraphics_item=None):
|
|
||||||
super(BaseNode, self).__init__(qgraphics_item or NodeItem)
|
|
||||||
self._inputs = []
|
|
||||||
self._outputs = []
|
|
||||||
|
|
||||||
def update_model(self):
|
|
||||||
"""
|
|
||||||
Update the node model from view.
|
|
||||||
"""
|
|
||||||
for name, val in self.view.properties.items():
|
|
||||||
if name in ['inputs', 'outputs']:
|
|
||||||
continue
|
|
||||||
self.model.set_property(name, val)
|
|
||||||
|
|
||||||
for name, widget in self.view.widgets.items():
|
|
||||||
self.model.set_property(name, widget.get_value())
|
|
||||||
|
|
||||||
def set_property(self, name, value, push_undo=True):
|
|
||||||
"""
|
|
||||||
Set the value on the node custom property.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): name of the property.
|
|
||||||
value (object): property data (python built in types).
|
|
||||||
push_undo (bool): register the command to the undo stack. (default: True)
|
|
||||||
"""
|
|
||||||
# prevent signals from causing a infinite loop.
|
|
||||||
if self.get_property(name) == value:
|
|
||||||
return
|
|
||||||
|
|
||||||
if name == 'visible':
|
|
||||||
if self.graph:
|
|
||||||
undo_cmd = NodeVisibleCmd(self, value)
|
|
||||||
if push_undo:
|
|
||||||
self.graph.undo_stack().push(undo_cmd)
|
|
||||||
else:
|
|
||||||
undo_cmd.redo()
|
|
||||||
return
|
|
||||||
elif name == 'disabled':
|
|
||||||
# redraw the connected pipes in the scene.
|
|
||||||
ports = self.view.inputs + self.view.outputs
|
|
||||||
for port in ports:
|
|
||||||
for pipe in port.connected_pipes:
|
|
||||||
pipe.update()
|
|
||||||
super(BaseNode, self).set_property(name, value, push_undo)
|
|
||||||
|
|
||||||
def set_layout_direction(self, value=0):
|
|
||||||
"""
|
|
||||||
Sets the node layout direction to either horizontal or vertical on
|
|
||||||
the current node only.
|
|
||||||
|
|
||||||
`Implemented in` ``v0.3.0``
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`NodeGraph.set_layout_direction`,
|
|
||||||
:meth:`NodeObject.layout_direction`
|
|
||||||
|
|
||||||
|
|
||||||
Warnings:
|
|
||||||
This function does not register to the undo stack.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (int): layout direction mode.
|
|
||||||
"""
|
|
||||||
# base logic to update the model and view attributes only.
|
|
||||||
super(BaseNode, self).set_layout_direction(value)
|
|
||||||
# redraw the node.
|
|
||||||
self._view.draw_node()
|
|
||||||
|
|
||||||
def set_icon(self, icon=None):
|
|
||||||
"""
|
|
||||||
Set the node icon.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
icon (str): path to the icon image.
|
|
||||||
"""
|
|
||||||
self.set_property('icon', icon)
|
|
||||||
|
|
||||||
def icon(self):
|
|
||||||
"""
|
|
||||||
Node icon path.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: icon image file path.
|
|
||||||
"""
|
|
||||||
return self.model.icon
|
|
||||||
|
|
||||||
def widgets(self):
|
|
||||||
"""
|
|
||||||
Returns all embedded widgets from this node.
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`BaseNode.get_widget`
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: embedded node widgets. {``property_name``: ``node_widget``}
|
|
||||||
"""
|
|
||||||
return self.view.widgets
|
|
||||||
|
|
||||||
def get_widget(self, name):
|
|
||||||
"""
|
|
||||||
Returns the embedded widget associated with the property name.
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`BaseNode.add_combo_menu`,
|
|
||||||
:meth:`BaseNode.add_text_input`,
|
|
||||||
:meth:`BaseNode.add_checkbox`,
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): node property name.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeBaseWidget: embedded node widget.
|
|
||||||
"""
|
|
||||||
return self.view.widgets.get(name)
|
|
||||||
|
|
||||||
def add_custom_widget(self, widget, widget_type=None, tab=None):
|
|
||||||
"""
|
|
||||||
Add a custom node widget into the node.
|
|
||||||
|
|
||||||
see example :ref:`Embedding Custom Widgets`.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
The ``value_changed`` signal from the added node widget is wired
|
|
||||||
up to the :meth:`NodeObject.set_property` function.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
widget (NodeBaseWidget): node widget class object.
|
|
||||||
widget_type: widget flag to display in the
|
|
||||||
:class:`NodeGraphQt.PropertiesBinWidget`
|
|
||||||
(default: :attr:`NodeGraphQt.constants.NodePropWidgetEnum.HIDDEN`).
|
|
||||||
tab (str): name of the widget tab to display in.
|
|
||||||
"""
|
|
||||||
if not isinstance(widget, NodeBaseWidget):
|
|
||||||
raise NodeWidgetError(
|
|
||||||
'\'widget\' must be an instance of a NodeBaseWidget')
|
|
||||||
|
|
||||||
widget_type = widget_type or NodePropWidgetEnum.HIDDEN.value
|
|
||||||
self.create_property(widget.get_name(),
|
|
||||||
widget.get_value(),
|
|
||||||
widget_type=widget_type,
|
|
||||||
tab=tab)
|
|
||||||
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
|
|
||||||
widget._node = self
|
|
||||||
self.view.add_widget(widget)
|
|
||||||
#: redraw node to address calls outside the "__init__" func.
|
|
||||||
self.view.draw_node()
|
|
||||||
|
|
||||||
#: HACK: calling the .parent() function here on the widget as it seems
|
|
||||||
# to address a seg fault issue when exiting the application.
|
|
||||||
widget.parent()
|
|
||||||
|
|
||||||
def add_combo_menu(self, name, label='', items=None, tooltip=None,
|
|
||||||
tab=None):
|
|
||||||
"""
|
|
||||||
Creates a custom property with the :meth:`NodeObject.create_property`
|
|
||||||
function and embeds a :class:`PySide2.QtWidgets.QComboBox` widget
|
|
||||||
into the node.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
The ``value_changed`` signal from the added node widget is wired
|
|
||||||
up to the :meth:`NodeObject.set_property` function.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): name for the custom property.
|
|
||||||
label (str): label to be displayed.
|
|
||||||
items (list[str]): items to be added into the menu.
|
|
||||||
tooltip (str): widget tooltip.
|
|
||||||
tab (str): name of the widget tab to display in.
|
|
||||||
"""
|
|
||||||
self.create_property(
|
|
||||||
name,
|
|
||||||
value=items[0] if items else None,
|
|
||||||
items=items or [],
|
|
||||||
widget_type=NodePropWidgetEnum.QCOMBO_BOX.value,
|
|
||||||
widget_tooltip=tooltip,
|
|
||||||
tab=tab
|
|
||||||
)
|
|
||||||
widget = NodeComboBox(self.view, name, label, items)
|
|
||||||
widget.setToolTip(tooltip or '')
|
|
||||||
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
|
|
||||||
self.view.add_widget(widget)
|
|
||||||
#: redraw node to address calls outside the "__init__" func.
|
|
||||||
self.view.draw_node()
|
|
||||||
|
|
||||||
def add_text_input(self, name, label='', text='', placeholder_text='',
|
|
||||||
tooltip=None, tab=None):
|
|
||||||
"""
|
|
||||||
Creates a custom property with the :meth:`NodeObject.create_property`
|
|
||||||
function and embeds a :class:`PySide2.QtWidgets.QLineEdit` widget
|
|
||||||
into the node.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
The ``value_changed`` signal from the added node widget is wired
|
|
||||||
up to the :meth:`NodeObject.set_property` function.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): name for the custom property.
|
|
||||||
label (str): label to be displayed.
|
|
||||||
text (str): pre-filled text.
|
|
||||||
placeholder_text (str): placeholder text.
|
|
||||||
tooltip (str): widget tooltip.
|
|
||||||
tab (str): name of the widget tab to display in.
|
|
||||||
"""
|
|
||||||
self.create_property(
|
|
||||||
name,
|
|
||||||
value=text,
|
|
||||||
widget_type=NodePropWidgetEnum.QLINE_EDIT.value,
|
|
||||||
widget_tooltip=tooltip,
|
|
||||||
tab=tab
|
|
||||||
)
|
|
||||||
widget = NodeLineEdit(self.view, name, label, text, placeholder_text)
|
|
||||||
widget.setToolTip(tooltip or '')
|
|
||||||
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
|
|
||||||
self.view.add_widget(widget)
|
|
||||||
#: redraw node to address calls outside the "__init__" func.
|
|
||||||
self.view.draw_node()
|
|
||||||
|
|
||||||
def add_checkbox(self, name, label='', text='', state=False, tooltip=None,
|
|
||||||
tab=None):
|
|
||||||
"""
|
|
||||||
Creates a custom property with the :meth:`NodeObject.create_property`
|
|
||||||
function and embeds a :class:`PySide2.QtWidgets.QCheckBox` widget
|
|
||||||
into the node.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
The ``value_changed`` signal from the added node widget is wired
|
|
||||||
up to the :meth:`NodeObject.set_property` function.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): name for the custom property.
|
|
||||||
label (str): label to be displayed.
|
|
||||||
text (str): checkbox text.
|
|
||||||
state (bool): pre-check.
|
|
||||||
tooltip (str): widget tooltip.
|
|
||||||
tab (str): name of the widget tab to display in.
|
|
||||||
"""
|
|
||||||
self.create_property(
|
|
||||||
name,
|
|
||||||
value=state,
|
|
||||||
widget_type=NodePropWidgetEnum.QCHECK_BOX.value,
|
|
||||||
widget_tooltip=tooltip,
|
|
||||||
tab=tab
|
|
||||||
)
|
|
||||||
widget = NodeCheckBox(self.view, name, label, text, state)
|
|
||||||
widget.setToolTip(tooltip or '')
|
|
||||||
widget.value_changed.connect(lambda k, v: self.set_property(k, v))
|
|
||||||
self.view.add_widget(widget)
|
|
||||||
#: redraw node to address calls outside the "__init__" func.
|
|
||||||
self.view.draw_node()
|
|
||||||
|
|
||||||
def hide_widget(self, name, push_undo=True):
|
|
||||||
"""
|
|
||||||
Hide an embedded node widget.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): node property name for the widget.
|
|
||||||
push_undo (bool): register the command to the undo stack. (default: True)
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`BaseNode.add_custom_widget`,
|
|
||||||
:meth:`BaseNode.show_widget`,
|
|
||||||
:meth:`BaseNode.get_widget`
|
|
||||||
"""
|
|
||||||
if not self.view.has_widget(name):
|
|
||||||
return
|
|
||||||
undo_cmd = NodeWidgetVisibleCmd(self, name, visible=False)
|
|
||||||
if push_undo:
|
|
||||||
self.graph.undo_stack().push(undo_cmd)
|
|
||||||
else:
|
|
||||||
undo_cmd.redo()
|
|
||||||
|
|
||||||
def show_widget(self, name, push_undo=True):
|
|
||||||
"""
|
|
||||||
Show an embedded node widget.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): node property name for the widget.
|
|
||||||
push_undo (bool): register the command to the undo stack. (default: True)
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`BaseNode.add_custom_widget`,
|
|
||||||
:meth:`BaseNode.hide_widget`,
|
|
||||||
:meth:`BaseNode.get_widget`
|
|
||||||
"""
|
|
||||||
if not self.view.has_widget(name):
|
|
||||||
return
|
|
||||||
undo_cmd = NodeWidgetVisibleCmd(self, name, visible=True)
|
|
||||||
if push_undo:
|
|
||||||
self.graph.undo_stack().push(undo_cmd)
|
|
||||||
else:
|
|
||||||
undo_cmd.redo()
|
|
||||||
|
|
||||||
def add_input(self, name='input', multi_input=False, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
"""
|
|
||||||
Add input :class:`Port` to node.
|
|
||||||
|
|
||||||
Warnings:
|
|
||||||
Undo is NOT supported for this function.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): name for the input port.
|
|
||||||
multi_input (bool): allow port to have more than one connection.
|
|
||||||
display_name (bool): display the port name on the node.
|
|
||||||
color (tuple): initial port color (r, g, b) ``0-255``.
|
|
||||||
locked (bool): locked state see :meth:`Port.set_locked`
|
|
||||||
painter_func (function or None): custom function to override the drawing
|
|
||||||
of the port shape see example: :ref:`Creating Custom Shapes`
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: the created port object.
|
|
||||||
"""
|
|
||||||
if name in self.inputs().keys():
|
|
||||||
raise PortRegistrationError(
|
|
||||||
'port name "{}" already registered.'.format(name))
|
|
||||||
|
|
||||||
port_args = [name, multi_input, display_name, locked]
|
|
||||||
if painter_func and callable(painter_func):
|
|
||||||
port_args.append(painter_func)
|
|
||||||
view = self.view.add_input(*port_args)
|
|
||||||
|
|
||||||
if color:
|
|
||||||
view.color = color
|
|
||||||
view.border_color = [min([255, max([0, i + 80])]) for i in color]
|
|
||||||
|
|
||||||
port = Port(self, view)
|
|
||||||
port.model.type_ = PortTypeEnum.IN.value
|
|
||||||
port.model.name = name
|
|
||||||
port.model.display_name = display_name
|
|
||||||
port.model.multi_connection = multi_input
|
|
||||||
port.model.locked = locked
|
|
||||||
self._inputs.append(port)
|
|
||||||
self.model.inputs[port.name()] = port.model
|
|
||||||
return port
|
|
||||||
|
|
||||||
def add_output(self, name='output', multi_output=True, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
"""
|
|
||||||
Add output :class:`Port` to node.
|
|
||||||
|
|
||||||
Warnings:
|
|
||||||
Undo is NOT supported for this function.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): name for the output port.
|
|
||||||
multi_output (bool): allow port to have more than one connection.
|
|
||||||
display_name (bool): display the port name on the node.
|
|
||||||
color (tuple): initial port color (r, g, b) ``0-255``.
|
|
||||||
locked (bool): locked state see :meth:`Port.set_locked`
|
|
||||||
painter_func (function or None): custom function to override the drawing
|
|
||||||
of the port shape see example: :ref:`Creating Custom Shapes`
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: the created port object.
|
|
||||||
"""
|
|
||||||
if name in self.outputs().keys():
|
|
||||||
raise PortRegistrationError(
|
|
||||||
'port name "{}" already registered.'.format(name))
|
|
||||||
|
|
||||||
port_args = [name, multi_output, display_name, locked]
|
|
||||||
if painter_func and callable(painter_func):
|
|
||||||
port_args.append(painter_func)
|
|
||||||
view = self.view.add_output(*port_args)
|
|
||||||
|
|
||||||
if color:
|
|
||||||
view.color = color
|
|
||||||
view.border_color = [min([255, max([0, i + 80])]) for i in color]
|
|
||||||
port = Port(self, view)
|
|
||||||
port.model.type_ = PortTypeEnum.OUT.value
|
|
||||||
port.model.name = name
|
|
||||||
port.model.display_name = display_name
|
|
||||||
port.model.multi_connection = multi_output
|
|
||||||
port.model.locked = locked
|
|
||||||
self._outputs.append(port)
|
|
||||||
self.model.outputs[port.name()] = port.model
|
|
||||||
return port
|
|
||||||
|
|
||||||
def get_input(self, port):
|
|
||||||
"""
|
|
||||||
Get input port by the name or index.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (str or int): port name or index.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: node port.
|
|
||||||
"""
|
|
||||||
if type(port) is int:
|
|
||||||
if port < len(self._inputs):
|
|
||||||
return self._inputs[port]
|
|
||||||
elif type(port) is str:
|
|
||||||
return self.inputs().get(port, None)
|
|
||||||
|
|
||||||
def get_output(self, port):
|
|
||||||
"""
|
|
||||||
Get output port by the name or index.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (str or int): port name or index.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: node port.
|
|
||||||
"""
|
|
||||||
if type(port) is int:
|
|
||||||
if port < len(self._outputs):
|
|
||||||
return self._outputs[port]
|
|
||||||
elif type(port) is str:
|
|
||||||
return self.outputs().get(port, None)
|
|
||||||
|
|
||||||
def delete_input(self, port):
|
|
||||||
"""
|
|
||||||
Delete input port.
|
|
||||||
|
|
||||||
Warnings:
|
|
||||||
Undo is NOT supported for this function.
|
|
||||||
|
|
||||||
You can only delete ports if :meth:`BaseNode.port_deletion_allowed`
|
|
||||||
returns ``True`` otherwise a port error is raised see also
|
|
||||||
:meth:`BaseNode.set_port_deletion_allowed`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (str or int): port name or index.
|
|
||||||
"""
|
|
||||||
if type(port) in [int, str]:
|
|
||||||
port = self.get_input(port)
|
|
||||||
if port is None:
|
|
||||||
return
|
|
||||||
if not self.port_deletion_allowed():
|
|
||||||
raise PortError(
|
|
||||||
'Port "{}" can\'t be deleted on this node because '
|
|
||||||
'"ports_removable" is not enabled.'.format(port.name()))
|
|
||||||
if port.locked():
|
|
||||||
raise PortError('Error: Can\'t delete a port that is locked!')
|
|
||||||
self._inputs.remove(port)
|
|
||||||
self._model.inputs.pop(port.name())
|
|
||||||
self._view.delete_input(port.view)
|
|
||||||
port.model.node = None
|
|
||||||
self._view.draw_node()
|
|
||||||
|
|
||||||
def delete_output(self, port):
|
|
||||||
"""
|
|
||||||
Delete output port.
|
|
||||||
|
|
||||||
Warnings:
|
|
||||||
Undo is NOT supported for this function.
|
|
||||||
|
|
||||||
You can only delete ports if :meth:`BaseNode.port_deletion_allowed`
|
|
||||||
returns ``True`` otherwise a port error is raised see also
|
|
||||||
:meth:`BaseNode.set_port_deletion_allowed`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (str or int): port name or index.
|
|
||||||
"""
|
|
||||||
if type(port) in [int, str]:
|
|
||||||
port = self.get_output(port)
|
|
||||||
if port is None:
|
|
||||||
return
|
|
||||||
if not self.port_deletion_allowed():
|
|
||||||
raise PortError(
|
|
||||||
'Port "{}" can\'t be deleted on this node because '
|
|
||||||
'"ports_removable" is not enabled.'.format(port.name()))
|
|
||||||
if port.locked():
|
|
||||||
raise PortError('Error: Can\'t delete a port that is locked!')
|
|
||||||
self._outputs.remove(port)
|
|
||||||
self._model.outputs.pop(port.name())
|
|
||||||
self._view.delete_output(port.view)
|
|
||||||
port.model.node = None
|
|
||||||
self._view.draw_node()
|
|
||||||
|
|
||||||
def set_port_deletion_allowed(self, mode=False):
|
|
||||||
"""
|
|
||||||
Allow ports to be removable on this node.
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`BaseNode.port_deletion_allowed` and
|
|
||||||
:meth:`BaseNode.set_ports`
|
|
||||||
|
|
||||||
Args:
|
|
||||||
mode (bool): true to allow.
|
|
||||||
"""
|
|
||||||
self.model.port_deletion_allowed = mode
|
|
||||||
|
|
||||||
def port_deletion_allowed(self):
|
|
||||||
"""
|
|
||||||
Return true if ports can be deleted on this node.
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`BaseNode.set_port_deletion_allowed`
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: true if ports can be deleted.
|
|
||||||
"""
|
|
||||||
return self.model.port_deletion_allowed
|
|
||||||
|
|
||||||
def set_ports(self, port_data):
|
|
||||||
"""
|
|
||||||
Create node input and output ports from serialized port data.
|
|
||||||
|
|
||||||
Warnings:
|
|
||||||
You can only use this function if the node has
|
|
||||||
:meth:`BaseNode.port_deletion_allowed` is `True`
|
|
||||||
see :meth:`BaseNode.set_port_deletion_allowed`
|
|
||||||
|
|
||||||
Hint:
|
|
||||||
example snippet of port data.
|
|
||||||
|
|
||||||
.. highlight:: python
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
{
|
|
||||||
'input_ports':
|
|
||||||
[{
|
|
||||||
'name': 'input',
|
|
||||||
'multi_connection': True,
|
|
||||||
'display_name': 'Input',
|
|
||||||
'locked': False
|
|
||||||
}],
|
|
||||||
'output_ports':
|
|
||||||
[{
|
|
||||||
'name': 'output',
|
|
||||||
'multi_connection': True,
|
|
||||||
'display_name': 'Output',
|
|
||||||
'locked': False
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port_data(dict): port data.
|
|
||||||
"""
|
|
||||||
if not self.port_deletion_allowed():
|
|
||||||
raise PortError(
|
|
||||||
'Ports cannot be set on this node because '
|
|
||||||
'"set_port_deletion_allowed" is not enabled on this node.')
|
|
||||||
|
|
||||||
for port in self._inputs:
|
|
||||||
self._view.delete_input(port.view)
|
|
||||||
port.model.node = None
|
|
||||||
for port in self._outputs:
|
|
||||||
self._view.delete_output(port.view)
|
|
||||||
port.model.node = None
|
|
||||||
self._inputs = []
|
|
||||||
self._outputs = []
|
|
||||||
self._model.outputs = {}
|
|
||||||
self._model.inputs = {}
|
|
||||||
|
|
||||||
[self.add_input(name=port['name'],
|
|
||||||
multi_input=port['multi_connection'],
|
|
||||||
display_name=port['display_name'],
|
|
||||||
locked=port.get('locked') or False)
|
|
||||||
for port in port_data['input_ports']]
|
|
||||||
[self.add_output(name=port['name'],
|
|
||||||
multi_output=port['multi_connection'],
|
|
||||||
display_name=port['display_name'],
|
|
||||||
locked=port.get('locked') or False)
|
|
||||||
for port in port_data['output_ports']]
|
|
||||||
self._view.draw_node()
|
|
||||||
|
|
||||||
def inputs(self):
|
|
||||||
"""
|
|
||||||
Returns all the input ports from the node.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {<port_name>: <port_object>}
|
|
||||||
"""
|
|
||||||
return {p.name(): p for p in self._inputs}
|
|
||||||
|
|
||||||
def input_ports(self):
|
|
||||||
"""
|
|
||||||
Return all input ports.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[NodeGraphQt.Port]: node input ports.
|
|
||||||
"""
|
|
||||||
return self._inputs
|
|
||||||
|
|
||||||
def outputs(self):
|
|
||||||
"""
|
|
||||||
Returns all the output ports from the node.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {<port_name>: <port_object>}
|
|
||||||
"""
|
|
||||||
return {p.name(): p for p in self._outputs}
|
|
||||||
|
|
||||||
def output_ports(self):
|
|
||||||
"""
|
|
||||||
Return all output ports.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[NodeGraphQt.Port]: node output ports.
|
|
||||||
"""
|
|
||||||
return self._outputs
|
|
||||||
|
|
||||||
def input(self, index):
|
|
||||||
"""
|
|
||||||
Return the input port with the matching index.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
index (int): index of the input port.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: port object.
|
|
||||||
"""
|
|
||||||
return self._inputs[index]
|
|
||||||
|
|
||||||
def set_input(self, index, port):
|
|
||||||
"""
|
|
||||||
Creates a connection pipe to the targeted output :class:`Port`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
index (int): index of the port.
|
|
||||||
port (NodeGraphQt.Port): port object.
|
|
||||||
"""
|
|
||||||
src_port = self.input(index)
|
|
||||||
src_port.connect_to(port)
|
|
||||||
|
|
||||||
def output(self, index):
|
|
||||||
"""
|
|
||||||
Return the output port with the matching index.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
index (int): index of the output port.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: port object.
|
|
||||||
"""
|
|
||||||
return self._outputs[index]
|
|
||||||
|
|
||||||
def set_output(self, index, port):
|
|
||||||
"""
|
|
||||||
Creates a connection pipe to the targeted input :class:`Port`.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
index (int): index of the port.
|
|
||||||
port (NodeGraphQt.Port): port object.
|
|
||||||
"""
|
|
||||||
src_port = self.output(index)
|
|
||||||
src_port.connect_to(port)
|
|
||||||
|
|
||||||
def connected_input_nodes(self):
|
|
||||||
"""
|
|
||||||
Returns all nodes connected from the input ports.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {<input_port>: <node_list>}
|
|
||||||
"""
|
|
||||||
nodes = OrderedDict()
|
|
||||||
for p in self.input_ports():
|
|
||||||
nodes[p] = [cp.node() for cp in p.connected_ports()]
|
|
||||||
return nodes
|
|
||||||
|
|
||||||
def connected_output_nodes(self):
|
|
||||||
"""
|
|
||||||
Returns all nodes connected from the output ports.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {<output_port>: <node_list>}
|
|
||||||
"""
|
|
||||||
nodes = OrderedDict()
|
|
||||||
for p in self.output_ports():
|
|
||||||
nodes[p] = [cp.node() for cp in p.connected_ports()]
|
|
||||||
return nodes
|
|
||||||
|
|
||||||
def add_accept_port_type(self, port, port_type_data):
|
|
||||||
"""
|
|
||||||
Add an accept constrain to a specified node port.
|
|
||||||
|
|
||||||
Once a constraint has been added only ports of that type specified will
|
|
||||||
be allowed a pipe connection.
|
|
||||||
|
|
||||||
port type data example
|
|
||||||
|
|
||||||
.. highlight:: python
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
{
|
|
||||||
'port_name': 'foo'
|
|
||||||
'port_type': PortTypeEnum.IN.value
|
|
||||||
'node_type': 'io.github.jchanvfx.NodeClass'
|
|
||||||
}
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`NodeGraphQt.BaseNode.accepted_port_types`
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (NodeGraphQt.Port): port to assign constrain to.
|
|
||||||
port_type_data (dict): port type data to accept a connection
|
|
||||||
"""
|
|
||||||
node_ports = self._inputs + self._outputs
|
|
||||||
if port not in node_ports:
|
|
||||||
raise PortError('Node does not contain port: "{}"'.format(port))
|
|
||||||
|
|
||||||
self._model.add_port_accept_connection_type(
|
|
||||||
port_name=port.name(),
|
|
||||||
port_type=port.type_(),
|
|
||||||
node_type=self.type_,
|
|
||||||
accept_pname=port_type_data['port_name'],
|
|
||||||
accept_ptype=port_type_data['port_type'],
|
|
||||||
accept_ntype=port_type_data['node_type']
|
|
||||||
)
|
|
||||||
|
|
||||||
def accepted_port_types(self, port):
|
|
||||||
"""
|
|
||||||
Returns a dictionary of connection constrains of the port types
|
|
||||||
that allow for a pipe connection to this node.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (NodeGraphQt.Port): port object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {<node_type>: {<port_type>: [<port_name>]}}
|
|
||||||
"""
|
|
||||||
ports = self._inputs + self._outputs
|
|
||||||
if port not in ports:
|
|
||||||
raise PortError('Node does not contain port "{}"'.format(port))
|
|
||||||
|
|
||||||
accepted_types = self.graph.model.port_accept_connection_types(
|
|
||||||
node_type=self.type_,
|
|
||||||
port_type=port.type_(),
|
|
||||||
port_name=port.name()
|
|
||||||
)
|
|
||||||
return accepted_types
|
|
||||||
|
|
||||||
def add_reject_port_type(self, port, port_type_data):
|
|
||||||
"""
|
|
||||||
Add a reject constrain to a specified node port.
|
|
||||||
|
|
||||||
Once a constraint has been added only ports of that type specified will
|
|
||||||
NOT be allowed a pipe connection.
|
|
||||||
|
|
||||||
port type data example
|
|
||||||
|
|
||||||
.. highlight:: python
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
{
|
|
||||||
'port_name': 'foo'
|
|
||||||
'port_type': PortTypeEnum.IN.value
|
|
||||||
'node_type': 'io.github.jchanvfx.NodeClass'
|
|
||||||
}
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`NodeGraphQt.Port.rejected_port_types`
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (NodeGraphQt.Port): port to assign constrain to.
|
|
||||||
port_type_data (dict): port type data to reject a connection
|
|
||||||
"""
|
|
||||||
node_ports = self._inputs + self._outputs
|
|
||||||
if port not in node_ports:
|
|
||||||
raise PortError('Node does not contain port: "{}"'.format(port))
|
|
||||||
|
|
||||||
self._model.add_port_reject_connection_type(
|
|
||||||
port_name=port.name(),
|
|
||||||
port_type=port.type_(),
|
|
||||||
node_type=self.type_,
|
|
||||||
reject_pname=port_type_data['port_name'],
|
|
||||||
reject_ptype=port_type_data['port_type'],
|
|
||||||
reject_ntype=port_type_data['node_type']
|
|
||||||
)
|
|
||||||
|
|
||||||
def rejected_port_types(self, port):
|
|
||||||
"""
|
|
||||||
Returns a dictionary of connection constrains of the port types
|
|
||||||
that are NOT allowed for a pipe connection to this node.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
port (NodeGraphQt.Port): port object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {<node_type>: {<port_type>: [<port_name>]}}
|
|
||||||
"""
|
|
||||||
ports = self._inputs + self._outputs
|
|
||||||
if port not in ports:
|
|
||||||
raise PortError('Node does not contain port "{}"'.format(port))
|
|
||||||
|
|
||||||
rejected_types = self.graph.model.port_reject_connection_types(
|
|
||||||
node_type=self.type_,
|
|
||||||
port_type=port.type_(),
|
|
||||||
port_name=port.name()
|
|
||||||
)
|
|
||||||
return rejected_types
|
|
||||||
|
|
||||||
def on_input_connected(self, in_port, out_port):
|
|
||||||
"""
|
|
||||||
Callback triggered when a new pipe connection is made.
|
|
||||||
|
|
||||||
*The default of this function does nothing re-implement if you require
|
|
||||||
logic to run for this event.*
|
|
||||||
|
|
||||||
Note:
|
|
||||||
to work with undo & redo for this method re-implement
|
|
||||||
:meth:`BaseNode.on_input_disconnected` with the reverse logic.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
in_port (NodeGraphQt.Port): source input port from this node.
|
|
||||||
out_port (NodeGraphQt.Port): output port that connected to this node.
|
|
||||||
"""
|
|
||||||
return
|
|
||||||
|
|
||||||
def on_input_disconnected(self, in_port, out_port):
|
|
||||||
"""
|
|
||||||
Callback triggered when a pipe connection has been disconnected
|
|
||||||
from a INPUT port.
|
|
||||||
|
|
||||||
*The default of this function does nothing re-implement if you require
|
|
||||||
logic to run for this event.*
|
|
||||||
|
|
||||||
Note:
|
|
||||||
to work with undo & redo for this method re-implement
|
|
||||||
:meth:`BaseNode.on_input_connected` with the reverse logic.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
in_port (NodeGraphQt.Port): source input port from this node.
|
|
||||||
out_port (NodeGraphQt.Port): output port that was disconnected.
|
|
||||||
"""
|
|
||||||
return
|
|
@ -9,7 +9,7 @@ If the API call is successful, the node's title is set to "Character Status (API
|
|||||||
If the API is down or returns an error, the title is set to "Character Status (API Disconnected)".
|
If the API is down or returns an error, the title is set to "Character Status (API Disconnected)".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from NodeGraphQt import BaseNode
|
from OdenGraphQt import BaseNode
|
||||||
from Qt import QtCore, QtGui
|
from Qt import QtCore, QtGui
|
||||||
import requests
|
import requests
|
||||||
import traceback
|
import traceback
|
||||||
@ -151,3 +151,4 @@ class CharacterStatusNode(BaseNode):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Error while handling port {stat}: {e}")
|
print(f"[ERROR] Error while handling port {stat}: {e}")
|
||||||
print("[ERROR] Stack Trace:\n", traceback.format_exc())
|
print("[ERROR] Stack Trace:\n", traceback.format_exc())
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ Behavior:
|
|||||||
- Displays the input value but does not transmit it further.
|
- Displays the input value but does not transmit it further.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from NodeGraphQt import BaseNode
|
from OdenGraphQt import BaseNode
|
||||||
|
|
||||||
class DataNode(BaseNode):
|
class DataNode(BaseNode):
|
||||||
__identifier__ = 'bunny-lab.io.data_node'
|
__identifier__ = 'bunny-lab.io.data_node'
|
||||||
@ -121,3 +121,5 @@ class DataNode(BaseNode):
|
|||||||
connected_node = connected_port.node()
|
connected_node = connected_port.node()
|
||||||
if hasattr(connected_node, 'receive_data'):
|
if hasattr(connected_node, 'receive_data'):
|
||||||
connected_node.receive_data(data, source_port_name)
|
connected_node.receive_data(data, source_port_name)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,176 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
from NodeGraphQt.nodes.base_node import BaseNode
|
|
||||||
from NodeGraphQt.nodes.port_node import PortInputNode, PortOutputNode
|
|
||||||
from NodeGraphQt.qgraphics.node_group import GroupNodeItem
|
|
||||||
|
|
||||||
|
|
||||||
class GroupNode(BaseNode):
|
|
||||||
"""
|
|
||||||
`Implemented in` ``v0.2.0``
|
|
||||||
|
|
||||||
The ``NodeGraphQt.GroupNode`` class extends from the :class:`NodeGraphQt.BaseNode`
|
|
||||||
class with the ability to nest other nodes inside of it.
|
|
||||||
|
|
||||||
.. inheritance-diagram:: NodeGraphQt.GroupNode
|
|
||||||
|
|
||||||
.. image:: ../_images/group_node.png
|
|
||||||
:width: 250px
|
|
||||||
|
|
||||||
-
|
|
||||||
"""
|
|
||||||
|
|
||||||
NODE_NAME = 'Group'
|
|
||||||
|
|
||||||
def __init__(self, qgraphics_item=None):
|
|
||||||
super(GroupNode, self).__init__(qgraphics_item or GroupNodeItem)
|
|
||||||
self._input_port_nodes = {}
|
|
||||||
self._output_port_nodes = {}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_expanded(self):
|
|
||||||
"""
|
|
||||||
Returns if the group node is expanded or collapsed.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: true if the node is expanded.
|
|
||||||
"""
|
|
||||||
if not self.graph:
|
|
||||||
return False
|
|
||||||
return bool(self.id in self.graph.sub_graphs)
|
|
||||||
|
|
||||||
def get_sub_graph(self):
|
|
||||||
"""
|
|
||||||
Returns the sub graph controller to the group node if initialized
|
|
||||||
or returns None.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
SubGraph: sub graph controller.
|
|
||||||
"""
|
|
||||||
return self.graph.sub_graphs.get(self.id)
|
|
||||||
|
|
||||||
def get_sub_graph_session(self):
|
|
||||||
"""
|
|
||||||
Returns the serialized sub graph session.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: serialized sub graph session.
|
|
||||||
"""
|
|
||||||
return self.model.subgraph_session
|
|
||||||
|
|
||||||
def set_sub_graph_session(self, serialized_session):
|
|
||||||
"""
|
|
||||||
Sets the sub graph session data to the group node.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
serialized_session (dict): serialized session.
|
|
||||||
"""
|
|
||||||
serialized_session = serialized_session or {}
|
|
||||||
self.model.subgraph_session = serialized_session
|
|
||||||
|
|
||||||
def expand(self):
|
|
||||||
"""
|
|
||||||
Expand the group node session.
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`NodeGraph.expand_group_node`,
|
|
||||||
:meth:`SubGraph.expand_group_node`.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
SubGraph: node graph used to manage the nodes expaneded session.
|
|
||||||
"""
|
|
||||||
sub_graph = self.graph.expand_group_node(self)
|
|
||||||
return sub_graph
|
|
||||||
|
|
||||||
def collapse(self):
|
|
||||||
"""
|
|
||||||
Collapse the group node session it's expanded child sub graphs.
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
:meth:`NodeGraph.collapse_group_node`,
|
|
||||||
:meth:`SubGraph.collapse_group_node`.
|
|
||||||
"""
|
|
||||||
self.graph.collapse_group_node(self)
|
|
||||||
|
|
||||||
def set_name(self, name=''):
|
|
||||||
super(GroupNode, self).set_name(name)
|
|
||||||
# update the tab bar and navigation labels.
|
|
||||||
sub_graph = self.get_sub_graph()
|
|
||||||
if sub_graph:
|
|
||||||
nav_widget = sub_graph.navigation_widget
|
|
||||||
nav_widget.update_label_item(self.name(), self.id)
|
|
||||||
|
|
||||||
if sub_graph.parent_graph.is_root:
|
|
||||||
root_graph = sub_graph.parent_graph
|
|
||||||
tab_bar = root_graph.widget.tabBar()
|
|
||||||
for idx in range(tab_bar.count()):
|
|
||||||
if tab_bar.tabToolTip(idx) == self.id:
|
|
||||||
tab_bar.setTabText(idx, self.name())
|
|
||||||
break
|
|
||||||
|
|
||||||
def add_input(self, name='input', multi_input=False, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
port = super(GroupNode, self).add_input(
|
|
||||||
name=name,
|
|
||||||
multi_input=multi_input,
|
|
||||||
display_name=display_name,
|
|
||||||
color=color,
|
|
||||||
locked=locked,
|
|
||||||
painter_func=painter_func
|
|
||||||
)
|
|
||||||
if self.is_expanded:
|
|
||||||
input_node = PortInputNode(parent_port=port)
|
|
||||||
input_node.NODE_NAME = port.name()
|
|
||||||
input_node.model.set_property('name', port.name())
|
|
||||||
input_node.add_output(port.name())
|
|
||||||
sub_graph = self.get_sub_graph()
|
|
||||||
sub_graph.add_node(input_node, selected=False, push_undo=False)
|
|
||||||
|
|
||||||
return port
|
|
||||||
|
|
||||||
def add_output(self, name='output', multi_output=True, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
port = super(GroupNode, self).add_output(
|
|
||||||
name=name,
|
|
||||||
multi_output=multi_output,
|
|
||||||
display_name=display_name,
|
|
||||||
color=color,
|
|
||||||
locked=locked,
|
|
||||||
painter_func=painter_func
|
|
||||||
)
|
|
||||||
if self.is_expanded:
|
|
||||||
output_port = PortOutputNode(parent_port=port)
|
|
||||||
output_port.NODE_NAME = port.name()
|
|
||||||
output_port.model.set_property('name', port.name())
|
|
||||||
output_port.add_input(port.name())
|
|
||||||
sub_graph = self.get_sub_graph()
|
|
||||||
sub_graph.add_node(output_port, selected=False, push_undo=False)
|
|
||||||
|
|
||||||
return port
|
|
||||||
|
|
||||||
def delete_input(self, port):
|
|
||||||
if type(port) in [int, str]:
|
|
||||||
port = self.get_input(port)
|
|
||||||
if port is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.is_expanded:
|
|
||||||
sub_graph = self.get_sub_graph()
|
|
||||||
port_node = sub_graph.get_node_by_port(port)
|
|
||||||
if port_node:
|
|
||||||
sub_graph.remove_node(port_node, push_undo=False)
|
|
||||||
|
|
||||||
super(GroupNode, self).delete_input(port)
|
|
||||||
|
|
||||||
def delete_output(self, port):
|
|
||||||
if type(port) in [int, str]:
|
|
||||||
port = self.get_output(port)
|
|
||||||
if port is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.is_expanded:
|
|
||||||
sub_graph = self.get_sub_graph()
|
|
||||||
port_node = sub_graph.get_node_by_port(port)
|
|
||||||
if port_node:
|
|
||||||
sub_graph.remove_node(port_node, push_undo=False)
|
|
||||||
|
|
||||||
super(GroupNode, self).delete_output(port)
|
|
@ -8,7 +8,7 @@ Math Operation Node:
|
|||||||
- Displays the computed result in a read-only text box labeled "Result".
|
- Displays the computed result in a read-only text box labeled "Result".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from NodeGraphQt import BaseNode
|
from OdenGraphQt import BaseNode
|
||||||
|
|
||||||
class MathOperationNode(BaseNode):
|
class MathOperationNode(BaseNode):
|
||||||
__identifier__ = 'bunny-lab.io.math_node'
|
__identifier__ = 'bunny-lab.io.math_node'
|
||||||
@ -119,3 +119,4 @@ class MathOperationNode(BaseNode):
|
|||||||
|
|
||||||
def receive_data(self, data, source_port_name=None):
|
def receive_data(self, data, source_port_name=None):
|
||||||
self.process_input()
|
self.process_input()
|
||||||
|
|
||||||
|
@ -1,135 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
from NodeGraphQt.errors import PortRegistrationError
|
|
||||||
from NodeGraphQt.nodes.base_node import BaseNode
|
|
||||||
from NodeGraphQt.qgraphics.node_port_in import PortInputNodeItem
|
|
||||||
from NodeGraphQt.qgraphics.node_port_out import PortOutputNodeItem
|
|
||||||
|
|
||||||
|
|
||||||
class PortInputNode(BaseNode):
|
|
||||||
"""
|
|
||||||
The ``PortInputNode`` is the node that represents a input port from a
|
|
||||||
:class:`NodeGraphQt.GroupNode` when expanded in a
|
|
||||||
:class:`NodeGraphQt.SubGraph`.
|
|
||||||
|
|
||||||
.. inheritance-diagram:: NodeGraphQt.nodes.port_node.PortInputNode
|
|
||||||
:parts: 1
|
|
||||||
|
|
||||||
.. image:: ../_images/port_in_node.png
|
|
||||||
:width: 150px
|
|
||||||
|
|
||||||
-
|
|
||||||
"""
|
|
||||||
|
|
||||||
NODE_NAME = 'InputPort'
|
|
||||||
|
|
||||||
def __init__(self, qgraphics_item=None, parent_port=None):
|
|
||||||
super(PortInputNode, self).__init__(qgraphics_item or PortInputNodeItem)
|
|
||||||
self._parent_port = parent_port
|
|
||||||
|
|
||||||
@property
|
|
||||||
def parent_port(self):
|
|
||||||
"""
|
|
||||||
The parent group node port representing this node.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: port object.
|
|
||||||
"""
|
|
||||||
return self._parent_port
|
|
||||||
|
|
||||||
def add_input(self, name='input', multi_input=False, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
"""
|
|
||||||
Warnings:
|
|
||||||
This is not available for the ``PortInputNode`` class.
|
|
||||||
"""
|
|
||||||
raise PortRegistrationError(
|
|
||||||
'"{}.add_input()" is not available for {}.'
|
|
||||||
.format(self.__class__.__name__, self)
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_output(self, name='output', multi_output=True, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
"""
|
|
||||||
Warnings:
|
|
||||||
This function is called by :meth:`NodeGraphQt.SubGraph.expand_group_node`
|
|
||||||
and is not available for the ``PortInputNode`` class.
|
|
||||||
"""
|
|
||||||
if self._outputs:
|
|
||||||
raise PortRegistrationError(
|
|
||||||
'"{}.add_output()" only ONE output is allowed for this node.'
|
|
||||||
.format(self.__class__.__name__, self)
|
|
||||||
)
|
|
||||||
super(PortInputNode, self).add_output(
|
|
||||||
name=name,
|
|
||||||
multi_output=multi_output,
|
|
||||||
display_name=False,
|
|
||||||
color=color,
|
|
||||||
locked=locked,
|
|
||||||
painter_func=None
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PortOutputNode(BaseNode):
|
|
||||||
"""
|
|
||||||
The ``PortOutputNode`` is the node that represents a output port from a
|
|
||||||
:class:`NodeGraphQt.GroupNode` when expanded in a
|
|
||||||
:class:`NodeGraphQt.SubGraph`.
|
|
||||||
|
|
||||||
.. inheritance-diagram:: NodeGraphQt.nodes.port_node.PortOutputNode
|
|
||||||
:parts: 1
|
|
||||||
|
|
||||||
.. image:: ../_images/port_out_node.png
|
|
||||||
:width: 150px
|
|
||||||
|
|
||||||
-
|
|
||||||
"""
|
|
||||||
|
|
||||||
NODE_NAME = 'OutputPort'
|
|
||||||
|
|
||||||
def __init__(self, qgraphics_item=None, parent_port=None):
|
|
||||||
super(PortOutputNode, self).__init__(
|
|
||||||
qgraphics_item or PortOutputNodeItem
|
|
||||||
)
|
|
||||||
self._parent_port = parent_port
|
|
||||||
|
|
||||||
@property
|
|
||||||
def parent_port(self):
|
|
||||||
"""
|
|
||||||
The parent group node port representing this node.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NodeGraphQt.Port: port object.
|
|
||||||
"""
|
|
||||||
return self._parent_port
|
|
||||||
|
|
||||||
def add_input(self, name='input', multi_input=False, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
"""
|
|
||||||
Warnings:
|
|
||||||
This function is called by :meth:`NodeGraphQt.SubGraph.expand_group_node`
|
|
||||||
and is not available for the ``PortOutputNode`` class.
|
|
||||||
"""
|
|
||||||
if self._inputs:
|
|
||||||
raise PortRegistrationError(
|
|
||||||
'"{}.add_input()" only ONE input is allowed for this node.'
|
|
||||||
.format(self.__class__.__name__, self)
|
|
||||||
)
|
|
||||||
super(PortOutputNode, self).add_input(
|
|
||||||
name=name,
|
|
||||||
multi_input=multi_input,
|
|
||||||
display_name=False,
|
|
||||||
color=color,
|
|
||||||
locked=locked,
|
|
||||||
painter_func=None
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_output(self, name='output', multi_output=True, display_name=True,
|
|
||||||
color=None, locked=False, painter_func=None):
|
|
||||||
"""
|
|
||||||
Warnings:
|
|
||||||
This is not available for the ``PortOutputNode`` class.
|
|
||||||
"""
|
|
||||||
raise PortRegistrationError(
|
|
||||||
'"{}.add_output()" is not available for {}.'
|
|
||||||
.format(self.__class__.__name__, self)
|
|
||||||
)
|
|
@ -28,7 +28,7 @@ import pkgutil
|
|||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
from Qt import QtWidgets, QtCore
|
from Qt import QtWidgets, QtCore
|
||||||
from NodeGraphQt import NodeGraph, BaseNode
|
from OdenGraphQt import NodeGraph, BaseNode
|
||||||
|
|
||||||
def import_nodes_from_folder(package_name):
|
def import_nodes_from_folder(package_name):
|
||||||
"""
|
"""
|
||||||
@ -58,6 +58,7 @@ def make_node_command(graph, nt):
|
|||||||
return command
|
return command
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
app = QtWidgets.QApplication([])
|
app = QtWidgets.QApplication([])
|
||||||
|
|
||||||
# Create the NodeGraph controller.
|
# Create the NodeGraph controller.
|
||||||
@ -104,3 +105,4 @@ if __name__ == '__main__':
|
|||||||
timer.start(500)
|
timer.start(500)
|
||||||
|
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 14 KiB |
Loading…
x
Reference in New Issue
Block a user