205 lines
7.3 KiB
Python
Executable File
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)
|