876 lines
28 KiB
Python
876 lines
28 KiB
Python
#!/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 |