Skip to content
Snippets Groups Projects
graphics_component_item.py 8.19 KiB
Newer Older
  • Learn to ignore specific revisions
  • Andreas Bolin's avatar
    Andreas Bolin committed
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    """
    B-ASIC Scheduler-gui Graphics Component Item Module.
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    Contains the scheduler-gui GraphicsComponentItem class for drawing and maintain a component in a graph.
    """
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from typing import Dict, List, Optional, Union
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    # QGraphics and QPainter imports
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from qtpy.QtCore import QPointF, Qt
    from qtpy.QtGui import QBrush, QColor, QCursor, QPainterPath, QPen
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    from qtpy.QtWidgets import (
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        QGraphicsEllipseItem,
        QGraphicsItem,
        QGraphicsItemGroup,
        QGraphicsPathItem,
        QGraphicsRectItem,
        QGraphicsSimpleTextItem,
    )
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    # B-ASIC
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from b_asic.graph_component import GraphComponent
    
    from b_asic.scheduler_gui._preferences import (
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        OPERATION_EXECUTION_TIME_INACTIVE,
        OPERATION_LATENCY_ACTIVE,
        OPERATION_LATENCY_INACTIVE,
    )
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    class GraphicsComponentItem(QGraphicsItemGroup):
    
        """Class to represent a component in a graph."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        _scale: float = 1.0
        """Static, changed from MainWindow."""
        _operation: GraphComponent
        _height: float
        _ports: Dict[
            str, Dict[str, Union[float, QPointF]]
        ]  # ['port-id']['latency/pos']
        _end_time: int
    
        _latency_item: QGraphicsPathItem
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        _execution_time_item: QGraphicsRectItem
        _label_item: QGraphicsSimpleTextItem
        _port_items: List[QGraphicsEllipseItem]
    
        def __init__(
            self,
            operation: GraphComponent,
            height: float = 0.75,
            parent: Optional[QGraphicsItem] = None,
        ):
    
            """
            Construct a GraphicsComponentItem. *parent* is passed to QGraphicsItemGroup's constructor.
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            super().__init__(parent=parent)
            self._operation = operation
            self._height = height
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            self._ports = {
                k: {"latency": float(v)}
                for k, v in operation.latency_offsets.items()
            }
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._end_time = max(operation.latency_offsets.values())
            self._port_items = []
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            self.setFlag(QGraphicsItem.ItemIsMovable)  # mouse move events
            self.setFlag(QGraphicsItem.ItemIsSelectable)  # mouse move events
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            # self.setAcceptHoverEvents(True)                 # mouse hover events
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            self.setAcceptedMouseButtons(
    
                Qt.MouseButton.LeftButton
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            )  # accepted buttons for movements
            self.setCursor(
    
                QCursor(Qt.CursorShape.OpenHandCursor)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            )  # default cursor when hovering over object
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
            self._make_component()
    
        # def sceneEvent(self, event: QEvent) -> bool:
        #     print(f'Component -->\t\t\t\t{event.type()}')
        #     # event.accept()
        #     # QApplication.sendEvent(self.scene(), event)
        #     return True
    
        def clear(self) -> None:
    
            """Sets all children's parent to None and delete the axis."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            for item in self.childItems():
                item.setParentItem(None)
                del item
    
        @property
        def op_id(self) -> str:
            """Get the op-id."""
            return self._operation.graph_id
    
        @property
        def operation(self) -> GraphComponent:
            """Get the operation."""
            return self._operation
    
        @property
        def height(self) -> float:
    
            """
            Get or set the current component height. Setting the height to a new
            value will update the component automatically.
            """
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            return self._height
    
        @height.setter
        def height(self, height: float) -> None:
            if self._height != height:
                self.clear()
                self._height = height
                self._make_component()
    
        @property
        def end_time(self) -> int:
            """Get the relative end time."""
            return self._end_time
    
        @property
        def event_items(self) -> List[QGraphicsItem]:
    
            """Returns a list of objects, that receives events."""
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            return [self]
    
        def get_port_location(self, key) -> QPointF:
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            return self.mapToParent(self._ports[key]["pos"])
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
        def set_active(self):
            self._set_background(OPERATION_LATENCY_ACTIVE)
    
            self.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor))
    
    
        def set_inactive(self):
            self._set_background(OPERATION_LATENCY_INACTIVE)
    
            self.setCursor(QCursor(Qt.CursorShape.OpenHandCursor))
    
    
        def _set_background(self, color: QColor):
            brush = QBrush(color)
    
            self._latency_item.setBrush(brush)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
        def _make_component(self) -> None:
            """Makes a new component out of the stored attributes."""
    
            latency_outline_pen = QPen(
                Qt.GlobalColor.black
            )  # used by component outline
            latency_outline_pen.setWidthF(2 / self._scale)
            # latency_outline_pen.setCapStyle(Qt.RoundCap)     # Qt.FlatCap, Qt.SquareCap (default), Qt.RoundCap
            latency_outline_pen.setJoinStyle(
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                Qt.RoundJoin
            )  # Qt.MiterJoin, Qt.BevelJoin (default), Qt.RoundJoin, Qt.SvgMiterJoin
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
            port_filling_brush = QBrush(
                Qt.GlobalColor.black
            )  # used by port filling
            port_outline_pen = QPen(Qt.GlobalColor.black)  # used by port outline
            port_outline_pen.setWidthF(0)
            # port_outline_pen.setCosmetic(True)
    
            port_size = 7 / self._scale  # the diameter of a port
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
            execution_time_color = QColor(OPERATION_EXECUTION_TIME_INACTIVE)
            execution_time_color.setAlpha(200)  # 0-255
            execution_time_pen = QPen()  # used by execution time outline
            execution_time_pen.setColor(execution_time_color)
            execution_time_pen.setWidthF(3 / self._scale)
    
    
            # Set the starting position
    
    
            latency, execution_time = self._operation.get_plot_coordinates()
            latency_path = QPainterPath(
    
                QPointF(latency[0][0], latency[0][1] * self._height)
    
            )  # starting point
            for _x, _y in latency[1:]:
    
                latency_path.lineTo(_x, _y * self._height)
    
            latency_path.closeSubpath()
            self._latency_item = QGraphicsPathItem(latency_path)
    
            self._latency_item.setPen(latency_outline_pen)
    
    
            if execution_time:
                execution_time_path = QPainterPath(
                    QPointF(
                        execution_time[0][0], execution_time[0][1] * self._height
                    )
                )  # starting point
                for _x, _y in execution_time[1:]:
                    execution_time_path.lineTo(_x, _y * self._height)
                execution_time_path.closeSubpath()
                self._execution_time_item = QGraphicsPathItem(execution_time_path)
    
                self._execution_time_item.setPen(execution_time_pen)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
            self._set_background(
    
                OPERATION_LATENCY_INACTIVE
    
            )  # used by component filling
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
            inputs, outputs = self._operation.get_io_coordinates()
            for i, (x, y) in enumerate(inputs):
                pos = QPointF(x, y * self._height)
                key = f"in{i}"
                self._ports[key]["pos"] = pos
                port_pos = self.mapToParent(pos)
                port = QGraphicsEllipseItem(
                    -port_size / 2, -port_size / 2, port_size, port_size
                )
                port.setPen(port_outline_pen)
                port.setBrush(port_filling_brush)
                port.setPos(port_pos.x(), port_pos.y())
                self._port_items.append(port)
    
            for i, (x, y) in enumerate(outputs):
                pos = QPointF(x, y * self._height)
                key = f"out{i}"
                self._ports[key]["pos"] = pos
                port_pos = self.mapToParent(pos)
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                port = QGraphicsEllipseItem(
                    -port_size / 2, -port_size / 2, port_size, port_size
    
                )
                port.setPen(port_outline_pen)
                port.setBrush(port_filling_brush)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                port.setPos(port_pos.x(), port_pos.y())
                self._port_items.append(port)
    
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            self._label_item = QGraphicsSimpleTextItem(self._operation.graph_id)
            self._label_item.setScale(self._label_item.scale() / self._scale)
    
            center = self._latency_item.boundingRect().center()
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            center -= self._label_item.boundingRect().center() / self._scale
    
            self._label_item.setPos(self._latency_item.pos() + center)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
    
    
            # item group, consist of component_item, port_items and execution_time_item
    
            self.addToGroup(self._latency_item)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
            for port in self._port_items:
                self.addToGroup(port)
            self.addToGroup(self._label_item)
    
    Andreas Bolin's avatar
    Andreas Bolin committed
                self.addToGroup(self._execution_time_item)