Start with signal lib
This commit is contained in:
@@ -5,7 +5,7 @@ from enum import Enum
|
||||
from itertools import pairwise
|
||||
from math import hypot
|
||||
|
||||
from PySide6.QtCore import QEvent, QObject, QPointF, QRectF, QSize, QTimer, Qt, Signal
|
||||
from PySide6.QtCore import QEvent, QObject, QPointF, QRectF, QTimer, Qt, Signal
|
||||
from PySide6.QtGui import QActionGroup, QBrush, QColor, QCursor, QDragEnterEvent, QDragMoveEvent, QDropEvent, QKeySequence, QMouseEvent, QPainter, QPainterPath, QPen, QShortcut, QWheelEvent
|
||||
from PySide6.QtWidgets import QGraphicsEllipseItem, QGraphicsItem, QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsScene, QGraphicsSceneMouseEvent, QGraphicsTextItem, QGraphicsView, QMenu, QWidget
|
||||
|
||||
@@ -13,7 +13,7 @@ from bedit_core.models import BondCausality, BondConnection, BondPort, Component
|
||||
from bedit_gui.models import Graph, GraphComponentLabel, GraphConnection, Icon
|
||||
from bedit_gui.services.component_clipboard import COMPONENTS_MIME
|
||||
from bedit_gui.ui.generated.ui_graph_editor_widget import Ui_graphEditorWidget
|
||||
from bedit_gui.utils.icon import get_pixmap_bounding_box, render_icon
|
||||
from bedit_gui.utils.icon import get_natural_icon_size, get_pixmap_bounding_box, render_icon
|
||||
|
||||
GRID_SPACING = 64
|
||||
SCENE_SIZE = 10000
|
||||
@@ -21,14 +21,18 @@ MIN_ZOOM = 0.2
|
||||
MAX_ZOOM = 4.0
|
||||
ZOOM_STEP = 1.15
|
||||
ZOOM_TO_FIT_PADDING = 32.0
|
||||
COMPONENT_ICON_SIZE = QSize(128, 128)
|
||||
COMPONENT_LABEL_FONT_SIZE = 24.0
|
||||
FALLBACK_COMPONENT_SPACING = 128
|
||||
CONNECTION_WIDTH = 4.0
|
||||
CONNECTION_BOUNDING_BOX_SPACING = 16.0
|
||||
BOND_CONNECTION_COLOR = "#000000"
|
||||
SIGNAL_CONNECTION_COLOR = "#00007f"
|
||||
BOND_CONNECTION_BOUNDING_BOX_SPACING = 16.0
|
||||
SIGNAL_CONNECTION_BOUNDING_BOX_SPACING = 0.0
|
||||
CONNECTION_STRAIGHTEN_TOLERANCE = 8.0
|
||||
ARROW_LENGTH = 32.0
|
||||
ARROW_HALF_WIDTH = 16.0
|
||||
SIGNAL_ARROW_LENGTH = ARROW_LENGTH / 2
|
||||
SIGNAL_ARROW_HALF_WIDTH = ARROW_HALF_WIDTH / 2
|
||||
CAUSALITY_TICK_HALF_LENGTH = 16.0
|
||||
|
||||
|
||||
@@ -66,8 +70,10 @@ class GraphConnectionItem(QGraphicsPathItem):
|
||||
self.connection_id = connection_id
|
||||
self.editor = editor
|
||||
self.setPath(self._connection_path(points, half_arrow, tick_at_source))
|
||||
pen = QPen(QColor("#202020"), CONNECTION_WIDTH)
|
||||
color = QColor(BOND_CONNECTION_COLOR if half_arrow else SIGNAL_CONNECTION_COLOR)
|
||||
pen = QPen(color, CONNECTION_WIDTH)
|
||||
self.setPen(pen)
|
||||
self.setBrush(QBrush(color))
|
||||
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
|
||||
self.setZValue(-1)
|
||||
|
||||
@@ -100,15 +106,17 @@ class GraphConnectionItem(QGraphicsPathItem):
|
||||
dx = target.x() - previous.x()
|
||||
dy = target.y() - previous.y()
|
||||
length = hypot(dx, dy)
|
||||
back_x = target.x() - ARROW_LENGTH * dx / length
|
||||
back_y = target.y() - ARROW_LENGTH * dy / length
|
||||
perpendicular_x = -ARROW_HALF_WIDTH * dy / length
|
||||
perpendicular_y = ARROW_HALF_WIDTH * dx / length
|
||||
arrow_length = ARROW_LENGTH if half_arrow else SIGNAL_ARROW_LENGTH
|
||||
arrow_half_width = ARROW_HALF_WIDTH if half_arrow else SIGNAL_ARROW_HALF_WIDTH
|
||||
back_x = target.x() - arrow_length * dx / length
|
||||
back_y = target.y() - arrow_length * dy / length
|
||||
perpendicular_x = -arrow_half_width * dy / length
|
||||
perpendicular_y = arrow_half_width * dx / length
|
||||
path.moveTo(target)
|
||||
path.lineTo(back_x + perpendicular_x, back_y + perpendicular_y)
|
||||
if not half_arrow:
|
||||
path.moveTo(target)
|
||||
path.lineTo(back_x - perpendicular_x, back_y - perpendicular_y)
|
||||
path.closeSubpath()
|
||||
if tick_at_source is not None:
|
||||
GraphConnectionItem._add_causality_tick(path, points, tick_at_source)
|
||||
return path
|
||||
@@ -315,6 +323,7 @@ class GraphEditorWidget(QWidget):
|
||||
self.scene = GraphGraphicsScene(self)
|
||||
self.scene.setSceneRect(-SCENE_SIZE / 2, -SCENE_SIZE / 2, SCENE_SIZE, SCENE_SIZE)
|
||||
self.ui.graphicsView.setScene(self.scene)
|
||||
self.ui.graphicsView.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
self.ui.graphicsView.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
|
||||
self.ui.graphicsView.viewport().installEventFilter(self)
|
||||
self.ui.graphicsView.viewport().setAcceptDrops(True)
|
||||
@@ -381,7 +390,8 @@ class GraphEditorWidget(QWidget):
|
||||
positions = {component_id: graph.component_positions.get(component_id, (index * FALLBACK_COMPONENT_SPACING, 0)) for index, component_id in enumerate(component.implementation.graph.components)}
|
||||
for component_id, child in component.implementation.graph.components.items():
|
||||
icon = icons.get(component_id, Icon())
|
||||
pixmap = render_icon(icon, child.interface.ports, COMPONENT_ICON_SIZE, render_ports=self._mode is GraphEditorMode.CONNECTION).pixmap(COMPONENT_ICON_SIZE)
|
||||
icon_size = get_natural_icon_size(icon)
|
||||
pixmap = render_icon(icon, child.interface.ports, icon_size, render_ports=self._mode is GraphEditorMode.CONNECTION).pixmap(icon_size)
|
||||
item = GraphComponentItem(component_id, pixmap, self)
|
||||
item.setOffset(-pixmap.width() / 2, -pixmap.height() / 2)
|
||||
item.setPos(*positions[component_id])
|
||||
@@ -435,7 +445,8 @@ class GraphEditorWidget(QWidget):
|
||||
points = self._straighten_direct_connection(points)
|
||||
source_bounds = self._component_bounds[source_component].translated(*source_position)
|
||||
target_bounds = self._component_bounds[target_component].translated(*target_position)
|
||||
points = self._clip_connection(points, source_bounds, target_bounds)
|
||||
spacing = BOND_CONNECTION_BOUNDING_BOX_SPACING if isinstance(connection, BondConnection) else SIGNAL_CONNECTION_BOUNDING_BOX_SPACING
|
||||
points = self._clip_connection(points, source_bounds, target_bounds, spacing)
|
||||
tick_at_source = None
|
||||
if isinstance(connection, BondConnection):
|
||||
tick_at_source = False if connection.causality is BondCausality.EFFORT_OUT else True if connection.causality is BondCausality.FLOW_OUT else None
|
||||
@@ -582,7 +593,7 @@ class GraphEditorWidget(QWidget):
|
||||
return points
|
||||
|
||||
@staticmethod
|
||||
def _clip_connection(points: list[tuple[float, float]], source_bounds: QRectF, target_bounds: QRectF) -> list[tuple[float, float]]:
|
||||
def _clip_connection(points: list[tuple[float, float]], source_bounds: QRectF, target_bounds: QRectF, spacing: float) -> list[tuple[float, float]]:
|
||||
source = points[0]
|
||||
target = points[-1]
|
||||
source_direction = next((point for point in points[1:] if point != source), None)
|
||||
@@ -590,13 +601,13 @@ class GraphEditorWidget(QWidget):
|
||||
if source_direction is None or target_direction is None:
|
||||
return points
|
||||
clipped = list(points)
|
||||
clipped[0] = GraphEditorWidget._bounding_box_edge(source_bounds, source, source_direction)
|
||||
clipped[-1] = GraphEditorWidget._bounding_box_edge(target_bounds, target, target_direction)
|
||||
clipped[0] = GraphEditorWidget._bounding_box_edge(source_bounds, source, source_direction, spacing)
|
||||
clipped[-1] = GraphEditorWidget._bounding_box_edge(target_bounds, target, target_direction, spacing)
|
||||
return clipped
|
||||
|
||||
@staticmethod
|
||||
def _bounding_box_edge(bounds: QRectF, origin: tuple[float, float], toward: tuple[float, float]) -> tuple[float, float]:
|
||||
bounds = bounds.adjusted(-CONNECTION_BOUNDING_BOX_SPACING, -CONNECTION_BOUNDING_BOX_SPACING, CONNECTION_BOUNDING_BOX_SPACING, CONNECTION_BOUNDING_BOX_SPACING)
|
||||
def _bounding_box_edge(bounds: QRectF, origin: tuple[float, float], toward: tuple[float, float], spacing: float) -> tuple[float, float]:
|
||||
bounds = bounds.adjusted(-spacing, -spacing, spacing, spacing)
|
||||
dx = toward[0] - origin[0]
|
||||
dy = toward[1] - origin[1]
|
||||
horizontal_scale = (bounds.right() - origin[0]) / dx if dx > 0 else (bounds.left() - origin[0]) / dx if dx < 0 else float("inf")
|
||||
@@ -664,7 +675,8 @@ class GraphEditorWidget(QWidget):
|
||||
self._connection_preview.setPath(QPainterPath(mouse_position))
|
||||
return
|
||||
bounds = self._component_bounds[self._connection_start].translated(*source)
|
||||
start = self._bounding_box_edge(bounds, source, (mouse_position.x(), mouse_position.y()))
|
||||
spacing = max(BOND_CONNECTION_BOUNDING_BOX_SPACING, SIGNAL_CONNECTION_BOUNDING_BOX_SPACING)
|
||||
start = self._bounding_box_edge(bounds, source, (mouse_position.x(), mouse_position.y()), spacing)
|
||||
path = QPainterPath(QPointF(*start))
|
||||
path.lineTo(mouse_position)
|
||||
self._connection_preview.setPath(path)
|
||||
|
||||
Reference in New Issue
Block a user