205 lines
7.3 KiB
Python
Executable File

from qtpy.QtGui import QPainterPath, QPainter, QPen, QTransform
from qtpy.QtWidgets import QGraphicsPathItem, QApplication
from qtpy.QtCore import QPointF
from enum import Enum
from ....core.Connection import Connection as CoreConnection
from . import colors
from ...Constants import (
CONNECTOR_ARROW_BASE,
CONNECTOR_ARROW_HEIGHT
)
class StyledConnection():
"""
Styled connection; current styles:
- CURVED: cubic spline
- ANGLED: rectangular connection, one step in the horizontal middle
"""
class ConnectionStyle(Enum):
CURVED = 0
ANGLED = 1
def __init__(self):
self._qsettings = QApplication.instance().qsettings
self._conn_style_str = self._qsettings.value("appearance/connection_style", "CURVED", type=str)
self._conn_style = self.ConnectionStyle[self._conn_style_str]
self._line_width_factor = self._qsettings.value("appearance/line_width_factor", 1.0, type=float)
class DummyConnection(QGraphicsPathItem, StyledConnection):
"""
Dummy connection used for when the user drags a connection
between two ports.
"""
def __init__(self, parent, start_point, end_point):
super(DummyConnection, self).__init__()
super(StyledConnection, self).__init__()
self.start_point = start_point
self.end_point = end_point
self._line = QPainterPath()
self._arrowhead = QPainterPath()
self._path = QPainterPath()
self.update(end_point)
self._color1 = self._color2 = None
self._current_port_rotations = self._current_coordinates = None
self._rel_points = None # connection coordinates relative to sink/source
self._arrow_rotation = 0.0 # rotation of the arrow in radians
self._current_cr = None # for what_is_selected() of curved line
self._line_path = None
self.setFlag(QGraphicsPathItem.ItemIsSelectable)
def update(self, end_point):
"""User moved the mouse, redraw with new end point"""
self.end_point = end_point
self._line.clear()
self._line.moveTo(self.start_point)
if self._conn_style == StyledConnection.ConnectionStyle.CURVED:
c1 = self.start_point + QPointF(200, 0)
c2 = self.end_point - QPointF(200, 0)
self._line.cubicTo(c1, c2, self.end_point)
elif self._conn_style == StyledConnection.ConnectionStyle.ANGLED:
y_start = self.start_point.y()
y_end = self.end_point.y()
delta_x = self.end_point.y() - self.start_point.x()
x_step = self.start_point.x() + delta_x / 2
self._line.lineTo(QPointF(x_step, y_start))
self._line.lineTo(QPointF(x_step, y_end))
self._line.lineTo(self.end_point)
self._arrowhead.clear()
self._arrowhead.moveTo(self.end_point)
self._arrowhead.lineTo(self.end_point + QPointF(-CONNECTOR_ARROW_HEIGHT, - CONNECTOR_ARROW_BASE / 2))
self._arrowhead.lineTo(self.end_point + QPointF(-CONNECTOR_ARROW_HEIGHT, CONNECTOR_ARROW_BASE / 2))
self._arrowhead.lineTo(self.end_point)
self._path.clear()
self._path.addPath(self._line)
self._path.addPath(self._arrowhead)
self.setPath(self._path)
def paint(self, painter, option, widget):
painter.setRenderHint(QPainter.Antialiasing)
color = colors.BORDER_COLOR
pen = QPen(color)
pen.setWidthF(2 * self._line_width_factor)
painter.setPen(pen)
painter.drawPath(self._line)
painter.setBrush(color)
painter.drawPath(self._arrowhead)
class Connection(CoreConnection):
def __init__(self, parent, source, sink):
super(Connection, self).__init__(parent, source, sink)
self.gui = GUIConnection(self, parent, source, sink)
class GUIConnection(QGraphicsPathItem, StyledConnection):
def __init__(self, core, parent, source, sink):
super(StyledConnection, self).__init__()
self.core = core
super(QGraphicsPathItem, self).__init__()
self.source = source
self.sink = sink
self._line = QPainterPath()
self._arrowhead = QPainterPath()
self._path = QPainterPath()
self._color1 = self._color2 = None
self._current_port_rotations = self._current_coordinates = None
self._rel_points = None # connection coordinates relative to sink/source
self._arrow_rotation = 0.0 # rotation of the arrow in radians
self._current_cr = None # for what_is_selected() of curved line
self._line_path = None
self.setFlag(QGraphicsPathItem.ItemIsSelectable)
self.update()
def update(self):
"""
Source and sink moved in relation to each other, redraw with new end points
"""
self._line.clear()
self._line.moveTo(self.source.gui.connection_point)
if self._conn_style == StyledConnection.ConnectionStyle.CURVED:
c1 = self.source.gui.ctrl_point
c2 = self.sink.gui.ctrl_point
self._line.cubicTo(c1, c2, self.sink.gui.connection_point)
elif self._conn_style == StyledConnection.ConnectionStyle.ANGLED:
start = self.source.gui.connection_point
end = self.sink.gui.connection_point
y_start = start.y()
y_end = end.y()
delta_x = end.x() - start.x()
x_step = start.x() + (delta_x / 2)
self._line.lineTo(QPointF(x_step, y_start))
self._line.lineTo(QPointF(x_step, y_end))
self._line.lineTo(end)
self._create_arrowhead()
self._path.clear()
self._path.addPath(self._line)
self._path.addPath(self._arrowhead)
self.setPath(self._path)
def set_rotation(self, angle):
self._arrow_rotation = angle
def _create_arrowhead(self):
self._arrowhead.clear()
end_point = self.sink.gui.connection_point
# Create a default arrow head path
default_arrowhead = QPainterPath()
default_arrowhead.moveTo(QPointF(10.0, 0))
default_arrowhead.lineTo(QPointF(10.0, 0) + QPointF(-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE / 2))
default_arrowhead.lineTo(QPointF(10.0, 0) + QPointF(-CONNECTOR_ARROW_HEIGHT, CONNECTOR_ARROW_BASE / 2))
default_arrowhead.lineTo(QPointF(10.0, 0))
# Rotate the default arrow head path based on _arrow_rotation
transform = QTransform()
transform.translate(end_point.x(), end_point.y())
transform.rotate(self._arrow_rotation)
self._arrowhead = transform.map(default_arrowhead)
def paint(self, painter, option, widget):
painter.setRenderHint(QPainter.Antialiasing)
color = colors.CONNECTION_ENABLED_COLOR
if self.isSelected():
color = colors.HIGHLIGHT_COLOR
elif not self.core.enabled:
color = colors.CONNECTION_DISABLED_COLOR
elif not self.core.is_valid():
color = colors.CONNECTION_ERROR_COLOR
pen = QPen(color)
pen.setWidthF(2 * self._line_width_factor)
painter.setPen(pen)
painter.drawPath(self._line)
painter.setBrush(color)
painter.drawPath(self._arrowhead)
def mouseDoubleClickEvent(self, e):
connection = self.core
self.core.parent.gui.set_saved(False)
connection.parent.connections.remove(connection)
self.scene().removeItem(self)