Source code for qtpynodeeditor.node_graphics_object

import typing

from qtpy.QtCore import QPoint, QRectF, QSize, QSizeF, Qt
from qtpy.QtGui import QCursor, QPainter
from qtpy.QtWidgets import (QGraphicsDropShadowEffect, QGraphicsItem,
                            QGraphicsObject, QGraphicsProxyWidget,
                            QGraphicsSceneContextMenuEvent,
                            QGraphicsSceneHoverEvent, QGraphicsSceneMouseEvent,
                            QSizePolicy, QStyleOptionGraphicsItem, QWidget)

from .enums import ConnectionPolicy
from .node_connection_interaction import NodeConnectionInteraction
from .port import PortType


[docs]class NodeGraphicsObject(QGraphicsObject): def __init__(self, scene, node): super().__init__() self._scene = scene self._node = node self._locked = False self._proxy_widget = None self._scene.addItem(self) self.setFlag(QGraphicsItem.ItemDoesntPropagateOpacityToChildren, True) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self._style = node.model.style node_style = self._style.node effect = QGraphicsDropShadowEffect() effect.setOffset(4, 4) effect.setBlurRadius(20) effect.setColor(node_style.shadow_color) self.setGraphicsEffect(effect) self.setOpacity(node_style.opacity) self.setAcceptHoverEvents(True) self.setZValue(0) self.embed_q_widget() # connect to the move signals to emit the move signals in FlowScene def on_move(): self._scene.node_moved.emit(self._node, self.pos()) self.xChanged.connect(on_move) self.yChanged.connect(on_move) def _cleanup(self): if self._scene is not None: self._scene.removeItem(self) self._scene = None
[docs] def setPos(self, pos): super().setPos(pos) self.move_connections()
@property def node(self): """ Node Returns ------- value : Node """ return self._node
[docs] def boundingRect(self) -> QRectF: """ boundingRect Returns ------- value : QRectF """ return self._node.geometry.bounding_rect
def set_geometry_changed(self): self.prepareGeometryChange()
[docs] def move_connections(self): """ Visits all attached connections and corrects their corresponding end points. """ for conn in self._node.state.all_connections: conn.graphics_object.move()
[docs] def lock(self, locked: bool): """ Lock Parameters ---------- locked : bool """ self._locked = locked self.setFlag(QGraphicsItem.ItemIsMovable, not locked) self.setFlag(QGraphicsItem.ItemIsFocusable, not locked) self.setFlag(QGraphicsItem.ItemIsSelectable, not locked)
[docs] def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: QWidget): """ Paint Parameters ---------- painter : QPainter option : QStyleOptionGraphicsItem widget : QWidget """ from .node_painter import NodePainter # TODO painter.setClipRect(option.exposedRect) NodePainter.paint(painter, self._node, self._scene, node_style=self._style.node, connection_style=self._style.connection, )
[docs] def itemChange(self, change: QGraphicsItem.GraphicsItemChange, value: typing.Any) -> typing.Any: """ itemChange Parameters ---------- change : QGraphicsItem.GraphicsItemChange value : any Returns ------- value : any """ if change == self.ItemPositionChange and self.scene(): self.move_connections() return super().itemChange(change, value)
[docs] def mousePressEvent(self, event: QGraphicsSceneMouseEvent): """ mousePressEvent Parameters ---------- event : QGraphicsSceneMouseEvent """ if self._locked: return # deselect all other items after self one is selected if not self.isSelected() and not (event.modifiers() & Qt.ControlModifier): self._scene.clearSelection() node_geometry = self._node.geometry for port_to_check in (PortType.input, PortType.output): # TODO do not pass sceneTransform port = node_geometry.check_hit_scene_point(port_to_check, event.scenePos(), self.sceneTransform()) if not port: continue connections = port.connections # start dragging existing connection if connections and port_to_check == PortType.input: conn, = connections interaction = NodeConnectionInteraction(self._node, conn, self._scene) interaction.disconnect(port_to_check) elif port_to_check == PortType.output: # initialize new Connection out_policy = port.connection_policy if connections and out_policy == ConnectionPolicy.one: conn, = connections self._scene.delete_connection(conn) # TODO_UPSTREAM: add to FlowScene connection = self._scene.create_connection(port) connection.graphics_object.grabMouse() pos = QPoint(event.pos().x(), event.pos().y()) geom = self._node.geometry state = self._node.state if self._node.model.resizable() and geom.resize_rect.contains(pos): state.resizing = True
[docs] def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): """ mouseMoveEvent Parameters ---------- event : QGraphicsSceneMouseEvent """ geom = self._node.geometry state = self._node.state if state.resizing: diff = event.pos() - event.lastPos() w = self._node.model.embedded_widget() if w: self.prepareGeometryChange() old_size = w.size() + QSize(diff.x(), diff.y()) w.setFixedSize(old_size) old_size_f = QSizeF(old_size) self._proxy_widget.setMinimumSize(old_size_f) self._proxy_widget.setMaximumSize(old_size_f) self._proxy_widget.setPos(geom.widget_position) geom.recalculate_size() self.update() self.move_connections() event.accept() else: super().mouseMoveEvent(event) if event.lastPos() != event.pos(): self.move_connections() event.ignore() bounding = self.mapToScene(self.boundingRect()).boundingRect() r = self.scene().sceneRect().united(bounding) self.scene().setSceneRect(r)
[docs] def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent): """ mouseReleaseEvent Parameters ---------- event : QGraphicsSceneMouseEvent """ state = self._node.state state.resizing = False super().mouseReleaseEvent(event) # position connections precisely after fast node move self.move_connections()
[docs] def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent): """ hoverEnterEvent Parameters ---------- event : QGraphicsSceneHoverEvent """ # void # bring all the colliding nodes to background overlap_items = self.collidingItems() for item in overlap_items: if item.zValue() > 0.0: item.setZValue(0.0) # bring self node forward self.setZValue(1.0) self._node.geometry.hovered = True self.update() self._scene.node_hovered.emit(self._node, event.screenPos()) event.accept()
[docs] def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): """ hoverLeaveEvent Parameters ---------- event : QGraphicsSceneHoverEvent """ self._node.geometry.hovered = False self.update() self._scene.node_hover_left.emit(self._node) event.accept()
[docs] def hoverMoveEvent(self, event: QGraphicsSceneHoverEvent): """ hoverMoveEvent Parameters ---------- q_graphics_scene_hover_event : QGraphicsSceneHoverEvent """ pos = event.pos() geom = self._node.geometry if (self._node.model.resizable() and geom.resize_rect.contains(QPoint(pos.x(), pos.y()))): self.setCursor(QCursor(Qt.SizeFDiagCursor)) else: self.setCursor(QCursor()) event.accept()
[docs] def mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent): """ mouseDoubleClickEvent Parameters ---------- event : QGraphicsSceneMouseEvent """ super().mouseDoubleClickEvent(event) self._scene.node_double_clicked.emit(self._node)
[docs] def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent): """ contextMenuEvent Parameters ---------- event : QGraphicsSceneContextMenuEvent """ self._scene.node_context_menu.emit( self._node, event.scenePos(), event.screenPos())
def embed_q_widget(self): geom = self._node.geometry widget = self._node.model.embedded_widget() if widget is None: return self._proxy_widget = QGraphicsProxyWidget(self) self._proxy_widget.setWidget(widget) self._proxy_widget.setPreferredWidth(5) geom.recalculate_size() # If the widget wants to use as much vertical space as possible, set it # to have the geomtry's equivalent_widget_height. if widget.sizePolicy().verticalPolicy() & QSizePolicy.ExpandFlag: self._proxy_widget.setMinimumHeight(geom.equivalent_widget_height()) self._proxy_widget.setPos(geom.widget_position) self.update() self._proxy_widget.setOpacity(1.0) self._proxy_widget.setFlag(QGraphicsItem.ItemIgnoresParentOpacity)