diff --git a/NodeGraphQt/base/model.py b/NodeGraphQt/base/model.py index a2df1217..69ffa1c8 100644 --- a/NodeGraphQt/base/model.py +++ b/NodeGraphQt/base/model.py @@ -19,6 +19,7 @@ def __init__(self, node): self.display_name = True self.multi_connection = False self.visible = True + self.locked = False self.connected_ports = defaultdict(list) self.data_type = 'NoneType' diff --git a/NodeGraphQt/base/node.py b/NodeGraphQt/base/node.py index 8650ca28..49fd197c 100644 --- a/NodeGraphQt/base/node.py +++ b/NodeGraphQt/base/node.py @@ -798,7 +798,8 @@ def add_checkbox(self, name, label='', text='', state=False, tab=None): self.view.add_widget(widget) def add_input(self, name='input', multi_input=False, display_name=True, - color=None, data_type='NoneType', painter_func=None): + color=None, data_type='NoneType', locked=False, + painter_func=None): """ Add input :class:`Port` to node. @@ -808,6 +809,7 @@ def add_input(self, name='input', multi_input=False, display_name=True, display_name (bool): display the port name on the node. color (tuple): initial port color (r, g, b) ``0-255``. data_type (str): port data type name. + locked (bool): locked state see :meth:`Port.set_locked` painter_func (function): custom function to override the drawing of the port shape see example: :ref:`Creating Custom Shapes` @@ -818,7 +820,7 @@ def add_input(self, name='input', multi_input=False, display_name=True, raise PortRegistrationError( 'port name "{}" already registered.'.format(name)) - port_args = [name, multi_input, display_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) @@ -833,12 +835,14 @@ def add_input(self, name='input', multi_input=False, display_name=True, port.model.display_name = display_name port.model.multi_connection = multi_input port.model.data_type = data_type + 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, data_type='NoneType', painter_func=None): + color=None, data_type='NoneType', locked=False, + painter_func=None): """ Add output :class:`Port` to node. @@ -848,6 +852,7 @@ def add_output(self, name='output', multi_output=True, display_name=True, display_name (bool): display the port name on the node. color (tuple): initial port color (r, g, b) ``0-255``. data_type(str): port data type name. + locked (bool): locked state see :meth:`Port.set_locked` painter_func (function): custom function to override the drawing of the port shape see example: :ref:`Creating Custom Shapes` @@ -858,7 +863,7 @@ def add_output(self, name='output', multi_output=True, display_name=True, raise PortRegistrationError( 'port name "{}" already registered.'.format(name)) - port_args = [name, multi_output, display_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) @@ -872,6 +877,7 @@ def add_output(self, name='output', multi_output=True, display_name=True, port.model.display_name = display_name port.model.multi_connection = multi_output port.model.data_type = data_type + port.model.locked = locked self._outputs.append(port) self.model.outputs[port.name()] = port.model return port diff --git a/NodeGraphQt/base/port.py b/NodeGraphQt/base/port.py index 3f30e508..d361193a 100644 --- a/NodeGraphQt/base/port.py +++ b/NodeGraphQt/base/port.py @@ -102,6 +102,15 @@ def visible(self): """ return self.model.visible + def locked(self): + """ + Port locked state. + + Returns: + bool: true if visible. + """ + return self.model.locked + def set_visible(self, visible=True): """ Sets weather the port should be visible or not. @@ -120,6 +129,21 @@ def set_visible(self, visible=True): undo_stack.push(PortVisibleCmd(self)) undo_stack.endMacro() + def set_locked(self, locked=False): + """ + Sets the port locked state when locked new pipe connections can't be + connected or disconnected from the node graph gui. + + Note: + pipes can still be connected/disconnected to locked ports through + the api. + + Args: + locked (Bool): true if locked. + """ + self.model.locked = locked + self.__view.locked = locked + def connected_ports(self): """ Returns all connected ports. diff --git a/NodeGraphQt/qgraphics/node_base.py b/NodeGraphQt/qgraphics/node_base.py index b9d5892f..d20fc8e1 100644 --- a/NodeGraphQt/qgraphics/node_base.py +++ b/NodeGraphQt/qgraphics/node_base.py @@ -692,7 +692,7 @@ def _add_port(self, port): return port def add_input(self, name='input', multi_port=False, display_name=True, - painter_func=None): + locked=False, painter_func=None): """ Adds a port qgraphics item into the node with the "port_type" set as IN_PORT. @@ -701,6 +701,7 @@ def add_input(self, name='input', multi_port=False, display_name=True, name (str): name for the port. multi_port (bool): allow multiple connections. display_name (bool): display the port name. + locked (bool): locked state. painter_func (function): custom paint function. Returns: @@ -714,10 +715,11 @@ def add_input(self, name='input', multi_port=False, display_name=True, port.port_type = IN_PORT port.multi_connection = multi_port port.display_name = display_name + port.locked = locked return self._add_port(port) def add_output(self, name='output', multi_port=False, display_name=True, - painter_func=None): + locked=False, painter_func=None): """ Adds a port qgraphics item into the node with the "port_type" set as OUT_PORT. @@ -726,6 +728,7 @@ def add_output(self, name='output', multi_port=False, display_name=True, name (str): name for the port. multi_port (bool): allow multiple connections. display_name (bool): display the port name. + locked (bool): locked state. painter_func (function): custom paint function. Returns: @@ -739,6 +742,7 @@ def add_output(self, name='output', multi_port=False, display_name=True, port.port_type = OUT_PORT port.multi_connection = multi_port port.display_name = display_name + port.locked = locked return self._add_port(port) def _delete_port(self, port, text): diff --git a/NodeGraphQt/qgraphics/port.py b/NodeGraphQt/qgraphics/port.py index fb4646ef..717a73f9 100644 --- a/NodeGraphQt/qgraphics/port.py +++ b/NodeGraphQt/qgraphics/port.py @@ -37,6 +37,7 @@ def __init__(self, parent=None): self._border_size = 1 self._port_type = None self._multi_connection = False + self._locked = False self.setCacheMode(ITEM_CACHE_MODE) @@ -222,6 +223,19 @@ def border_size(self): def border_size(self, size=2): self._border_size = size + @property + def locked(self): + return self._locked + + @locked.setter + def locked(self, value=False): + self._locked = value + conn_type = 'multi' if self.multi_connection else 'single' + tooltip = '{}: ({})'.format(self.name, conn_type) + if value: + tooltip += ' (L)' + self.setToolTip(tooltip) + @property def multi_connection(self): return self._multi_connection diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index 7b2e167a..076d22ed 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -186,11 +186,13 @@ def _on_search_submitted(self, node_type): self.search_triggered.emit(node_type, (pos.x(), pos.y())) def _on_pipes_sliced(self, path): - self.connection_sliced.emit([ - [i.input_port, i.output_port] - for i in self.scene().items(path) - if isinstance(i, Pipe) and i != self._LIVE_PIPE - ]) + ports = [] + for i in self.scene().items(path): + if isinstance(i, Pipe) and i != self._LIVE_PIPE: + if any([i.input_port.locked, i.output_port.locked]): + continue + ports.append([i.input_port, i.output_port]) + self.connection_sliced.emit(ports) # --- reimplemented events --- @@ -534,9 +536,11 @@ def sceneMousePressEvent(self, event): # pipe slicer enabled. if self.ALT_state and self.SHIFT_state: return + # viewer pan mode. if self.ALT_state: return + if self._LIVE_PIPE.isVisible(): self.apply_live_connection(event) return @@ -545,6 +549,10 @@ def sceneMousePressEvent(self, event): port_items = self._items_near(pos, PortItem, 5, 5) if port_items and self.editable: port = port_items[0] + + if port.locked: + return + if not port.multi_connection and port.connected_ports: self._detached_port = port.connected_ports[0] self.start_live_connection(port) @@ -573,6 +581,10 @@ def sceneMousePressEvent(self, event): return pipe = pipe_items[0] from_port = pipe.port_from_pos(pos, True) + + if from_port.locked: + return + from_port.hovered = True attr = {IN_PORT: 'output_port', OUT_PORT: 'input_port'} @@ -648,6 +660,8 @@ def apply_live_connection(self, event): # restore connection check. restore_connection = any([ + # if the end port is locked. + end_port.locked, # if same port type. end_port.port_type == self._start_port.port_type, # if connection to itself.