Source code for qtpynodeeditor.node_painter

import math
import typing

from qtpy.QtCore import QPointF, QRectF, Qt
from qtpy.QtGui import QFontMetrics, QLinearGradient, QPainter, QPen

from .enums import NodeValidationState, PortType
from .node_data import NodeDataModel
from .node_geometry import NodeGeometry
from .node_graphics_object import NodeGraphicsObject
from .node_state import NodeState
from .style import ConnectionStyle, NodeStyle

if typing.TYPE_CHECKING:
    from .connection import Connection  # noqa
    from .flow_scene import FlowScene  # noqa
    from .node import Node  # noqa


[docs]class NodePainterDelegate:
[docs] def paint(self, painter: QPainter, geom: NodeGeometry, model: NodeDataModel): """ Paint Parameters ---------- painter : QPainter geom : NodeGeometry model : NodeDataModel """ ...
[docs]class NodePainter:
[docs] @staticmethod def paint(painter: QPainter, node: 'Node', scene: 'FlowScene', node_style: NodeStyle, connection_style: ConnectionStyle): """ Paint Parameters ---------- painter : QPainter node : Node scene : FlowScene node_style : NodeStyle connection_style : ConnectionStyle """ geom = node.geometry state = node.state graphics_object = node.graphics_object geom.recalculate_size(painter.font()) model = node.model NodePainter.draw_node_rect(painter, geom, model, graphics_object, node_style) NodePainter.draw_connection_points(painter, geom, state, model, scene, node_style, connection_style) NodePainter.draw_filled_connection_points(painter, geom, state, model, node_style, connection_style ) NodePainter.draw_model_name(painter, geom, state, model, node_style) NodePainter.draw_entry_labels(painter, geom, state, model, node_style) NodePainter.draw_resize_rect(painter, geom, model) NodePainter.draw_validation_rect(painter, geom, model, graphics_object, node_style) # call custom painter painter_delegate = model.painter_delegate() if painter_delegate: painter_delegate.paint(painter, geom, model)
[docs] @staticmethod def draw_node_rect(painter: QPainter, geom: NodeGeometry, model: NodeDataModel, graphics_object: NodeGraphicsObject, node_style: NodeStyle): """ Draw node rect Parameters ---------- painter : QPainter geom : NodeGeometry model : NodeDataModel graphics_object : NodeGraphicsObject node_style : NodeStyle """ color = (node_style.selected_boundary_color if graphics_object.isSelected() else node_style.normal_boundary_color ) p = QPen(color, (node_style.hovered_pen_width if geom.hovered else node_style.pen_width)) painter.setPen(p) gradient = QLinearGradient(QPointF(0.0, 0.0), QPointF(2.0, geom.height)) for at_, color in node_style.gradient_colors: gradient.setColorAt(at_, color) painter.setBrush(gradient) diam = node_style.connection_point_diameter boundary = QRectF(-diam, -diam, 2.0 * diam + geom.width, 2.0 * diam + geom.height) radius = 3.0 painter.drawRoundedRect(boundary, radius, radius)
[docs] @staticmethod def draw_model_name(painter: QPainter, geom: NodeGeometry, state: NodeState, model: NodeDataModel, node_style: NodeStyle): """ Draw model name Parameters ---------- painter : QPainter geom : NodeGeometry state : NodeState model : NodeDataModel """ if not model.caption_visible: return name = model.caption f = painter.font() f.setBold(True) metrics = QFontMetrics(f) rect = metrics.boundingRect(name) position = QPointF((geom.width - rect.width()) / 2.0, (geom.spacing + geom.entry_height) / 3.0) painter.setFont(f) painter.setPen(node_style.font_color) painter.drawText(position, name) f.setBold(False) painter.setFont(f)
[docs] @staticmethod def draw_entry_labels(painter: QPainter, geom: NodeGeometry, state: NodeState, model: NodeDataModel, node_style: NodeStyle): """ Draw entry labels Parameters ---------- painter : QPainter geom : NodeGeometry state : NodeState model : NodeDataModel node_style : NodeStyle """ metrics = painter.fontMetrics() for port in state.ports: scene_pos = port.scene_position if not port.connections: painter.setPen(node_style.font_color_faded) else: painter.setPen(node_style.font_color) display_text = port.display_text rect = metrics.boundingRect(display_text) scene_pos.setY(scene_pos.y() + rect.height() / 4.0) if port.port_type == PortType.input: scene_pos.setX(5.0) elif port.port_type == PortType.output: scene_pos.setX(geom.width - 5.0 - rect.width()) painter.drawText(scene_pos, display_text)
[docs] @staticmethod def draw_connection_points(painter: QPainter, geom: NodeGeometry, state: NodeState, model: NodeDataModel, scene: 'FlowScene', node_style: NodeStyle, connection_style: ConnectionStyle ): """ Draw connection points Parameters ---------- painter : QPainter geom : NodeGeometry state : NodeState model : NodeDataModel scene : FlowScene connection_style : ConnectionStyle """ diameter = node_style.connection_point_diameter reduced_diameter = diameter * 0.6 for port in state.ports: scene_pos = port.scene_position can_connect = port.can_connect port_type = port.port_type data_type = port.data_type r = 1.0 if state.is_reacting and can_connect and port_type == state.reacting_port_type: diff = geom.dragging_pos - scene_pos dist = math.sqrt(QPointF.dotProduct(diff, diff)) registry = scene.registry dtype1, dtype2 = state.reacting_data_type, data_type if port_type != PortType.input: dtype2, dtype1 = dtype1, dtype2 type_convertable = registry.get_type_converter(dtype1, dtype2) is not None if dtype1.id == dtype2.id or type_convertable: thres = 40.0 r = ((2.0 - dist / thres) if dist < thres else 1.0) else: thres = 80.0 r = ((dist / thres) if dist < thres else 1.0) if connection_style.use_data_defined_colors: brush = connection_style.get_normal_color(data_type.id) else: brush = node_style.connection_point_color painter.setBrush(brush) painter.drawEllipse(scene_pos, reduced_diameter * r, reduced_diameter * r)
[docs] @staticmethod def draw_filled_connection_points(painter: QPainter, geom: NodeGeometry, state: NodeState, model: NodeDataModel, node_style: NodeStyle, connection_style: ConnectionStyle ): """ Draw filled connection points Parameters ---------- painter : QPainter geom : NodeGeometry state : NodeState model : NodeDataModel node_style : NodeStyle connection_style : ConnectionStyle """ diameter = node_style.connection_point_diameter for port in state.ports: if not port.connections: continue scene_pos = port.scene_position if connection_style.use_data_defined_colors: c = connection_style.get_normal_color(port.data_type.id) else: c = node_style.filled_connection_point_color painter.setPen(c) painter.setBrush(c) painter.drawEllipse(scene_pos, diameter * 0.4, diameter * 0.4)
[docs] @staticmethod def draw_resize_rect(painter: QPainter, geom: NodeGeometry, model: NodeDataModel): """ Draw resize rect Parameters ---------- painter : QPainter geom : NodeGeometry model : NodeDataModel """ if model.resizable(): painter.setBrush(Qt.gray) painter.drawEllipse(geom.resize_rect)
[docs] @staticmethod def draw_validation_rect(painter: QPainter, geom: NodeGeometry, model: NodeDataModel, graphics_object: NodeGraphicsObject, node_style: NodeStyle): """ Draw validation rect Parameters ---------- painter : QPainter geom : NodeGeometry model : NodeDataModel graphics_object : NodeGraphicsObject node_style : NodeStyle """ model_validation_state = model.validation_state() if model_validation_state == NodeValidationState.valid: return color = (node_style.selected_boundary_color if graphics_object.isSelected() else node_style.normal_boundary_color) if geom.hovered: p = QPen(color, node_style.hovered_pen_width) else: p = QPen(color, node_style.pen_width) painter.setPen(p) # Drawing the validation message background if model_validation_state == NodeValidationState.error: painter.setBrush(node_style.error_color) else: painter.setBrush(node_style.warning_color) radius = 3.0 diam = node_style.connection_point_diameter boundary = QRectF( -diam, -diam + geom.height - geom.validation_height, 2.0 * diam + geom.width, 2.0 * diam + geom.validation_height, ) painter.drawRoundedRect(boundary, radius, radius) painter.setBrush(Qt.gray) # Drawing the validation message itself error_msg = model.validation_message() f = painter.font() metrics = QFontMetrics(f) rect = metrics.boundingRect(error_msg) position = QPointF( (geom.width - rect.width()) / 2.0, geom.height - (geom.validation_height - diam) / 2.0 ) painter.setFont(f) painter.setPen(node_style.font_color) painter.drawText(position, error_msg)