Labels
This commit is contained in:
@@ -6,10 +6,10 @@ from math import hypot
|
||||
|
||||
from PySide6.QtCore import QEvent, QObject, QPointF, QRectF, QSize, QTimer, Qt, Signal
|
||||
from PySide6.QtGui import QActionGroup, QBrush, QColor, QCursor, QKeySequence, QMouseEvent, QPainter, QPainterPath, QPen, QShortcut, QWheelEvent
|
||||
from PySide6.QtWidgets import QGraphicsEllipseItem, QGraphicsItem, QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsScene, QGraphicsSceneMouseEvent, QGraphicsView, QMenu, QWidget
|
||||
from PySide6.QtWidgets import QGraphicsEllipseItem, QGraphicsItem, QGraphicsPathItem, QGraphicsPixmapItem, QGraphicsScene, QGraphicsSceneMouseEvent, QGraphicsTextItem, QGraphicsView, QMenu, QWidget
|
||||
|
||||
from bedit_core.models import BondCausality, BondConnection, BondPort, Component, ComponentID, Connection, ConnectionID, GraphImplementation, Port, PortID, SignalConnection, SignalDirection, SignalPort
|
||||
from bedit_gui.models import Graph, GraphConnection, Icon
|
||||
from bedit_gui.models import Graph, GraphComponentLabel, GraphConnection, Icon
|
||||
from bedit_gui.ui.generated.ui_graph_editor_widget import Ui_graphEditorWidget
|
||||
from bedit_gui.utils.icon import get_pixmap_bounding_box, render_icon
|
||||
|
||||
@@ -20,6 +20,7 @@ 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
|
||||
@@ -184,6 +185,58 @@ class GraphComponentItem(QGraphicsPixmapItem):
|
||||
event.accept()
|
||||
|
||||
|
||||
class GraphComponentLabelItem(QGraphicsTextItem):
|
||||
def __init__(self, component_id: ComponentID, text: str, label: GraphComponentLabel, component_item: GraphComponentItem, editor: GraphEditorWidget) -> None:
|
||||
super().__init__(text, component_item)
|
||||
self.component_id = component_id
|
||||
self.editor = editor
|
||||
self._dragging = False
|
||||
self._drag_start = label.relative_position
|
||||
font = self.font()
|
||||
font.setItalic(True)
|
||||
font.setPointSizeF(COMPONENT_LABEL_FONT_SIZE)
|
||||
self.setFont(font)
|
||||
self.setDefaultTextColor(QColor("#202020"))
|
||||
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
|
||||
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable, editor.mode is GraphEditorMode.NORMAL)
|
||||
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges)
|
||||
self.set_relative_position(label.relative_position)
|
||||
|
||||
def relative_position(self) -> tuple[int, int]:
|
||||
component_bounds = self.editor._component_bounds[self.component_id]
|
||||
return round(self.pos().x() + self.boundingRect().width() / 2), round(self.pos().y() - component_bounds.bottom())
|
||||
|
||||
def set_relative_position(self, position: tuple[int, int]) -> None:
|
||||
component_bounds = self.editor._component_bounds[self.component_id]
|
||||
self.setPos(position[0] - self.boundingRect().width() / 2, component_bounds.bottom() + position[1])
|
||||
|
||||
def itemChange(self, change: QGraphicsItem.GraphicsItemChange, value: object) -> object:
|
||||
if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange and self._dragging and isinstance(value, QPointF):
|
||||
component_bounds = self.editor._component_bounds[self.component_id]
|
||||
size = self.editor.snap_to_grid_size
|
||||
relative_x = value.x() + self.boundingRect().width() / 2
|
||||
relative_y = value.y() - component_bounds.bottom()
|
||||
value = QPointF(round(relative_x / size) * size - self.boundingRect().width() / 2, component_bounds.bottom() + round(relative_y / size) * size)
|
||||
return super().itemChange(change, value)
|
||||
|
||||
def mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None:
|
||||
self._drag_start = self.relative_position()
|
||||
self._dragging = True
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
|
||||
super().mouseReleaseEvent(event)
|
||||
self._dragging = False
|
||||
position = self.relative_position()
|
||||
self.set_relative_position(position)
|
||||
if position != self._drag_start:
|
||||
self.editor.finish_component_label_move(self.component_id, position)
|
||||
|
||||
def contextMenuEvent(self, event) -> None:
|
||||
self.editor.component_context_menu_requested.emit(self.component_id, event.screenPos())
|
||||
event.accept()
|
||||
|
||||
|
||||
class GraphConnectionPointItem(QGraphicsEllipseItem):
|
||||
def __init__(self, connection_id: ConnectionID, index: int, position: tuple[int, int], editor: GraphEditorWidget) -> None:
|
||||
radius = CONNECTION_WIDTH
|
||||
@@ -234,6 +287,7 @@ class GraphEditorWidget(QWidget):
|
||||
component_move_requested = Signal(object, object, object)
|
||||
component_context_menu_requested = Signal(object, object)
|
||||
component_open_requested = Signal(object)
|
||||
component_label_move_requested = Signal(object, object, object)
|
||||
connection_points_change_requested = Signal(object, object, object, str)
|
||||
connection_add_requested = Signal(object, object)
|
||||
connections_delete_requested = Signal(object, object)
|
||||
@@ -251,6 +305,7 @@ class GraphEditorWidget(QWidget):
|
||||
self._connection_preview: QGraphicsPathItem | None = None
|
||||
self._component_items: dict[ComponentID, GraphComponentItem] = {}
|
||||
self._component_bounds: dict[ComponentID, QRectF] = {}
|
||||
self._component_label_items: dict[ComponentID, GraphComponentLabelItem] = {}
|
||||
self._connection_items: dict[ConnectionID, GraphConnectionItem] = {}
|
||||
self._connection_point_items: dict[ConnectionID, list[GraphConnectionPointItem]] = {}
|
||||
self.set_snap_to_grid_size(snap_to_grid_size)
|
||||
@@ -310,6 +365,7 @@ class GraphEditorWidget(QWidget):
|
||||
self._icons = icons or {}
|
||||
self._component_items = {}
|
||||
self._component_bounds = {}
|
||||
self._component_label_items = {}
|
||||
self._connection_items = {}
|
||||
self._connection_point_items = {}
|
||||
self.scene.clear()
|
||||
@@ -331,6 +387,11 @@ class GraphEditorWidget(QWidget):
|
||||
self._component_bounds[component_id] = bounds.translated(-pixmap.width() / 2, -pixmap.height() / 2)
|
||||
self.scene.addItem(item)
|
||||
|
||||
for component_id, child in component.implementation.graph.components.items():
|
||||
label = graph.component_labels.get(component_id, GraphComponentLabel())
|
||||
if label.visible:
|
||||
self._create_component_label_item(component_id, child.name, label)
|
||||
|
||||
for connection_id, connection in component.implementation.graph.connections.items():
|
||||
if not isinstance(connection, (SignalConnection, BondConnection)):
|
||||
continue
|
||||
@@ -459,6 +520,31 @@ class GraphEditorWidget(QWidget):
|
||||
if self._component is not None:
|
||||
self.component_move_requested.emit(self._component, component_id, position)
|
||||
|
||||
def finish_component_label_move(self, component_id: ComponentID, relative_position: tuple[int, int]) -> None:
|
||||
if self._component is not None:
|
||||
self.component_label_move_requested.emit(self._component, component_id, relative_position)
|
||||
|
||||
def component_label_visible(self, component_id: ComponentID) -> bool:
|
||||
return self._graph.component_labels.get(component_id, GraphComponentLabel()).visible
|
||||
|
||||
def set_component_label(self, component_id: ComponentID, label: GraphComponentLabel | None) -> None:
|
||||
if label is None:
|
||||
self._graph.component_labels.pop(component_id, None)
|
||||
label = GraphComponentLabel()
|
||||
else:
|
||||
self._graph.component_labels[component_id] = label
|
||||
item = self._component_label_items.pop(component_id, None)
|
||||
if item is not None:
|
||||
item.setParentItem(None)
|
||||
self.scene.removeItem(item)
|
||||
component = self._component
|
||||
if component is not None and label.visible and component_id in component.implementation.graph.components:
|
||||
self._create_component_label_item(component_id, component.implementation.graph.components[component_id].name, label)
|
||||
|
||||
def _create_component_label_item(self, component_id: ComponentID, text: str, label: GraphComponentLabel) -> None:
|
||||
item = GraphComponentLabelItem(component_id, text, label, self._component_items[component_id], self)
|
||||
self._component_label_items[component_id] = item
|
||||
|
||||
def set_component_position(self, component_id: ComponentID, position: tuple[int, int] | None) -> None:
|
||||
item = self._component_items.get(component_id)
|
||||
if item is None:
|
||||
|
||||
Reference in New Issue
Block a user