From a518bcc2ff4ba00153fb0f930bb4426125b75d6d Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson <oscar.gustafsson@gmail.com> Date: Fri, 2 Sep 2022 08:50:04 +0200 Subject: [PATCH] More cleanup and refactoring --- b_asic/__init__.py | 2 +- b_asic/core_operations.py | 2 + b_asic/operation.py | 3 +- .../scheduler-gui/graphics_component_item.py | 93 +++++++------------ b_asic/scheduler-gui/graphics_graph_event.py | 75 ++++++--------- b_asic/scheduler-gui/graphics_graph_item.py | 14 +-- b_asic/scheduler-gui/main_window.py | 1 - b_asic/special_operations.py | 4 + 8 files changed, 77 insertions(+), 117 deletions(-) diff --git a/b_asic/__init__.py b/b_asic/__init__.py index 7f4b9555..51bebc26 100644 --- a/b_asic/__init__.py +++ b/b_asic/__init__.py @@ -5,7 +5,7 @@ ASIC toolbox that simplifies circuit design and optimization. # NOTE: If this import gives an error, # make sure the C++ module has been compiled and installed properly. # See the included README.md for more information on how to build/install. -from _b_asic import * +# from _b_asic import * # Python modules. from b_asic.core_operations import * from b_asic.graph_component import * diff --git a/b_asic/core_operations.py b/b_asic/core_operations.py index b89ade62..c2530c16 100644 --- a/b_asic/core_operations.py +++ b/b_asic/core_operations.py @@ -20,6 +20,8 @@ class Constant(AbstractOperation): output(0): self.param("value") """ + _execution_time = 0 + def __init__(self, value: Number = 0, name: Name = ""): """Construct a Constant operation with the given value.""" super().__init__(input_count=0, output_count=1, name=name, latency_offsets={'out0' : 0}) diff --git a/b_asic/operation.py b/b_asic/operation.py index dd4c9fc7..92a20a36 100644 --- a/b_asic/operation.py +++ b/b_asic/operation.py @@ -290,7 +290,7 @@ class AbstractOperation(Operation, AbstractGraphComponent): _input_ports: List[InputPort] _output_ports: List[OutputPort] - _execution_time: Union[int, None] + _execution_time: Union[int, None] = None def __init__(self, input_count: int, output_count: int, name: Name = "", input_sources: Optional[Sequence[Optional[SignalSourceProvider]]] = None, latency: Optional[int] = None, latency_offsets: Optional[Dict[str, int]] = None): """Construct an operation with the given input/output count. @@ -305,7 +305,6 @@ class AbstractOperation(Operation, AbstractGraphComponent): self._input_ports = [InputPort(self, i) for i in range(input_count)] self._output_ports = [OutputPort(self, i) for i in range(output_count)] - self._execution_time = None # Connect given input sources, if any. if input_sources is not None: diff --git a/b_asic/scheduler-gui/graphics_component_item.py b/b_asic/scheduler-gui/graphics_component_item.py index 7b64f10f..8f342950 100644 --- a/b_asic/scheduler-gui/graphics_component_item.py +++ b/b_asic/scheduler-gui/graphics_component_item.py @@ -4,39 +4,18 @@ Contains the scheduler-gui GraphicsComponentItem class for drawing and maintain a component in a graph. """ -import os -import sys -from typing import Any, Optional -from pprint import pprint from typing import ( - Any, Union, Optional, overload, final, Dict, OrderedDict, List) -# from typing_extensions import Self, Final, Literal, LiteralString, TypeAlias, final -import numpy as np - -import qtpy -from qtpy import QtCore -from qtpy import QtGui -from qtpy import QtWidgets + Union, Optional, Dict, List) # QGraphics and QPainter imports -from qtpy.QtCore import ( - Qt, QObject, QRect, QRectF, QPoint, QSize, QSizeF, QByteArray, Slot, QEvent) -from qtpy.QtGui import ( - QPaintEvent, QPainter, QPainterPath, QColor, QBrush, QPen, QFont, QPolygon, QIcon, QPixmap, - QLinearGradient, QTransform, QCursor) +from qtpy.QtCore import Qt, QPointF +from qtpy.QtGui import QPainterPath, QColor, QBrush, QPen, QCursor from qtpy.QtWidgets import ( - QGraphicsView, QGraphicsScene, QGraphicsWidget, - QGraphicsLayout, QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayoutItem, QGraphicsAnchorLayout, QGraphicsItem, QGraphicsItemGroup, QGraphicsPathItem, QGraphicsRectItem, - QStyleOptionGraphicsItem, QWidget, QGraphicsObject, QGraphicsSceneMouseEvent, QGraphicsSimpleTextItem, - QGraphicsEllipseItem) -from qtpy.QtCore import ( - QPoint, QPointF) + QGraphicsSimpleTextItem, QGraphicsEllipseItem) # B-ASIC -import logger -from b_asic.schedule import Schedule -# from b_asic.graph_component import GraphComponent +from b_asic.graph_component import GraphComponent class GraphicsComponentItem(QGraphicsItemGroup): @@ -52,18 +31,15 @@ class GraphicsComponentItem(QGraphicsItemGroup): _execution_time_item: QGraphicsRectItem _label_item: QGraphicsSimpleTextItem _port_items: List[QGraphicsEllipseItem] - - - def __init__(self, op_id: str, latency_offsets: Dict[str, int], execution_time: Optional[int] = None, height: float = 0.75, parent: Optional[QGraphicsItem] = None): + + + def __init__(self, operation: GraphComponent, height: float = 0.75, parent: Optional[QGraphicsItem] = None): """Constructs a GraphicsComponentItem. 'parent' is passed to QGraphicsItemGroup's constructor.""" super().__init__(parent=parent) - self._op_id = op_id + self._operation = operation self._height = height - self._ports = {k:{'latency':float(v)} for k,v in latency_offsets.items()} - self._end_time = 0 - for latency in latency_offsets.values(): - self._end_time = max(self._end_time, latency) - self._execution_time = execution_time + self._ports = {k:{'latency':float(v)} for k,v in operation.latency_offsets.items()} + self._end_time = max(operation.latency_offsets.values()) self._port_items = [] self.setFlag(QGraphicsItem.ItemIsMovable) # mouse move events @@ -71,46 +47,47 @@ class GraphicsComponentItem(QGraphicsItemGroup): # self.setAcceptHoverEvents(True) # mouse hover events self.setAcceptedMouseButtons(Qt.LeftButton) # accepted buttons for movements self.setCursor(QCursor(Qt.OpenHandCursor)) # default cursor when hovering over object - + 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.""" for item in self.childItems(): item.setParentItem(None) del item - + @property def op_id(self) -> str: """Get the op-id.""" - return self._op_id - + return self._operation.graph_id + @property def height(self) -> float: """Get or set the current component height. Setting the height to a new value will update the component automatically.""" 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]: """Returnes a list of objects, that receives events.""" @@ -123,22 +100,21 @@ class GraphicsComponentItem(QGraphicsItemGroup): pen1.setWidthF(2/self._scale) # pen1.setCapStyle(Qt.RoundCap) # Qt.FlatCap, Qt.SquareCap (default), Qt.RoundCap pen1.setJoinStyle(Qt.RoundJoin) # Qt.MiterJoin, Qt.BevelJoin (default), Qt.RoundJoin, Qt.SvgMiterJoin - + brush2 = QBrush(Qt.black) # used by port filling pen2 = QPen(Qt.black) # used by port outline pen2.setWidthF(0) # pen2.setCosmetic(True) port_size = 7/self._scale # the diameter of an port - + gray = QColor(Qt.gray) gray.setAlpha(100) # 0-255 - brush3 = QBrush(gray) # used by execution time green = QColor(Qt.magenta) green.setAlpha(200) # 0-255 pen3 = QPen() # used by execution time outline pen3.setColor(green) pen3.setWidthF(3/self._scale) - + ## component path def draw_component_path(keys: List[str], revered: bool) -> None: @@ -169,14 +145,17 @@ class GraphicsComponentItem(QGraphicsItemGroup): input_keys = sorted(input_keys) output_keys = [key for key in self._ports.keys() if key.lower().startswith("out")] output_keys = sorted(output_keys, reverse=True) - + # Set the starting position - x = self._ports[input_keys[0]]['latency'] + if input_keys: + x = self._ports[input_keys[0]]['latency'] + else: + x = 0 y = 0 old_x = x old_y = y component_path = QPainterPath(QPointF(x, y)) # starting point - + # draw the path draw_component_path(input_keys, False) # draw input side draw_component_path(output_keys, True) # draw ouput side @@ -198,22 +177,22 @@ class GraphicsComponentItem(QGraphicsItemGroup): self._port_items[-1].setPos(port_pos.x(), port_pos.y()) ## op-id/label - self._label_item = QGraphicsSimpleTextItem(self._op_id) + self._label_item = QGraphicsSimpleTextItem(self._operation.graph_id) self._label_item.setScale(self._label_item.scale() / self._scale) center = self._component_item.boundingRect().center() center -= self._label_item.boundingRect().center() / self._scale self._label_item.setPos(self._component_item.pos() + center) - + ## execution time - if self._execution_time: - self._execution_time_item = QGraphicsRectItem(0, 0, self._execution_time, self._height) + if self._operation.execution_time: + self._execution_time_item = QGraphicsRectItem(0, 0, self._operation.execution_time, self._height) self._execution_time_item.setPen(pen3) # self._execution_time_item.setBrush(brush3) - + ## item group, consist of component_item, port_items and execution_time_item self.addToGroup(self._component_item) for port in self._port_items: self.addToGroup(port) self.addToGroup(self._label_item) - if self._execution_time: + if self._operation.execution_time: self.addToGroup(self._execution_time_item) diff --git a/b_asic/scheduler-gui/graphics_graph_event.py b/b_asic/scheduler-gui/graphics_graph_event.py index 0d4de3d0..f1b1cfdb 100644 --- a/b_asic/scheduler-gui/graphics_graph_event.py +++ b/b_asic/scheduler-gui/graphics_graph_event.py @@ -5,38 +5,15 @@ Contains the scheduler-gui GraphicsGraphEvent class containing event filters and handlers for GraphicsGraphItem objects. """ -import os -import sys -from typing import Any, Optional -from pprint import pprint -from typing import Any, Union, Optional, overload, Final, final, List -# from typing_extensions import Self, Final, Literal, LiteralString, TypeAlias, final -import numpy as np -from copy import deepcopy -from itertools import combinations - -import qtpy -from qtpy import QtCore -from qtpy import QtGui -from qtpy import QtWidgets +from typing import Optional, overload, List # QGraphics and QPainter imports -from qtpy.QtCore import ( - Qt, QObject, QRect, QRectF, QPoint, QSize, QSizeF, QByteArray, Signal, Slot, QEvent) -from qtpy.QtGui import ( - QPaintEvent, QPainter, QPainterPath, QColor, QBrush, QPen, QFont, QPolygon, QIcon, QPixmap, - QLinearGradient, QTransform, QCursor, QFocusEvent) +from qtpy.QtCore import Qt, QObject, Signal, QEvent, QPointF +from qtpy.QtGui import QCursor, QFocusEvent from qtpy.QtWidgets import ( - QGraphicsView, QGraphicsScene, QGraphicsWidget, - QGraphicsLayout, QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayoutItem, QGraphicsAnchorLayout, - QGraphicsItem, QGraphicsItemGroup, QGraphicsPathItem, QGraphicsLineItem, QGraphicsRectItem, - QStyleOptionGraphicsItem, QWidget, - QGraphicsObject, QGraphicsSceneMouseEvent, QGraphicsSceneHoverEvent, QGraphicsSceneContextMenuEvent, - QGraphicsSceneDragDropEvent, QGraphicsSceneWheelEvent) -from qtpy.QtCore import ( - QPoint, QPointF) - -from abc import ABC + QGraphicsItem, QGraphicsSceneMouseEvent, QGraphicsSceneHoverEvent, + QGraphicsSceneContextMenuEvent, QGraphicsSceneDragDropEvent, QGraphicsSceneWheelEvent) + from graphics_component_item import GraphicsComponentItem from graphics_axes_item import GraphicsAxesItem from graphics_timeline_item import GraphicsTimelineItem @@ -51,7 +28,7 @@ class GraphicsGraphEvent: # PyQt5 """A class respresenting signals.""" component_selected = Signal(str) schedule_time_changed = Signal() - + _axes: GraphicsAxesItem _current_pos: QPointF _delta_time: int @@ -59,12 +36,12 @@ class GraphicsGraphEvent: # PyQt5 # component_selected = Signal(str) # PySide2 # schedule_time_changed = Signal() # PySide2 - - @overload + + #@overload def is_component_valid_pos(self, pos: float, end_time: int) -> bool: ... - @overload + #@overload def is_valid_delta_time(self, delta_time: int) -> bool: ... - @overload + #@overload def set_schedule_time(self, delta_time: int) -> None: ... # def __init__(self, parent: Optional[QGraphicsItem] = None): # PySide2 @@ -91,7 +68,7 @@ class GraphicsGraphEvent: # PyQt5 item: GraphicsComponentItem for item in filterItems: item.installSceneEventFilter(self) - + @overload def removeSceneEventFilters(self, filterItems: QGraphicsItem) -> None: ... @overload @@ -102,8 +79,8 @@ class GraphicsGraphEvent: # PyQt5 item: GraphicsComponentItem for item in filterItems: item.removeSceneEventFilter(self) - - + + def sceneEventFilter(self, item: QGraphicsItem, event: QEvent) -> bool: """Returns true if the event was filtered (i.e. stopped), otherwise false. If false is returned, the event is forwarded to the appropriate child in @@ -128,7 +105,7 @@ class GraphicsGraphEvent: # PyQt5 # QEvent.GraphicsSceneWheel: self.comp_wheelEvent } handler = switch.get(event.type()) - + elif isinstance(item, GraphicsTimelineItem): # the timeline switch = { # QEvent.GraphicsSceneHoverEnter: self.timeline_hoverEnterEvent, @@ -138,11 +115,11 @@ class GraphicsGraphEvent: # PyQt5 QEvent.GraphicsSceneMouseRelease: self.timeline_mouseReleaseEvent, } handler = switch.get(event.type()) - + else: raise TypeError(f"Received an unexpected event '{event.type()}' " f"from an '{type(item).__name__}' object.") - + if handler is not None: handler(event) return True @@ -169,7 +146,7 @@ class GraphicsGraphEvent: # PyQt5 def comp_hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent) -> None: ... def comp_mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: - """Set the position of the graphical element in the graphic scene, + """Set the position of the graphical element in the graphic scene, translate coordinates of the cursor within the graphic element in the coordinate system of the parent object. The object can only move horizontally in x-axis scale steps.""" @@ -189,7 +166,7 @@ class GraphicsGraphEvent: # PyQt5 # self.prepareGeometryChange() item.setX(pos) self._current_pos.setX(self._current_pos.x() - 1.0) - + def comp_mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None: """Changes the cursor to ClosedHandCursor when grabbing an object and stores the current position in item's parent coordinates. 'event' will @@ -201,7 +178,7 @@ class GraphicsGraphEvent: # PyQt5 self._current_pos = item.mapToParent(event.pos()) item.setCursor(QCursor(Qt.ClosedHandCursor)) event.accept() - + def comp_mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: """Changes the cursor to OpenHandCursor when releasing an object.""" item: GraphicsComponentItem = self.scene().mouseGrabberItem() @@ -209,14 +186,14 @@ class GraphicsGraphEvent: # PyQt5 def comp_mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent) -> None: ... def comp_wheelEvent(self, event: QGraphicsSceneWheelEvent) -> None: ... - - - + + + ############################################### #### Event Handlers: GraphicsLineTem #### ############################################### def timeline_mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: - """Set the position of the graphical element in the graphic scene, + """Set the position of the graphical element in the graphic scene, translate coordinates of the cursor within the graphic element in the coordinate system of the parent object. The object can only move horizontally in x-axis scale steps.""" @@ -240,7 +217,7 @@ class GraphicsGraphEvent: # PyQt5 self._current_pos.setX(self._current_pos.x() - 1.0) self._delta_time -= 1 item.set_text(self._delta_time) - + def timeline_mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None: """Stores the current position in item's parent coordinates. 'event' will by default be accepted, and this item is then the mouse grabber. This @@ -251,7 +228,7 @@ class GraphicsGraphEvent: # PyQt5 item.show_label() self._current_pos = item.mapToParent(event.pos()) event.accept() - + def timeline_mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: """Updates the schedule time.""" item: GraphicsTimelineItem = self.scene().mouseGrabberItem() diff --git a/b_asic/scheduler-gui/graphics_graph_item.py b/b_asic/scheduler-gui/graphics_graph_item.py index c6708cab..a15f3f0c 100644 --- a/b_asic/scheduler-gui/graphics_graph_item.py +++ b/b_asic/scheduler-gui/graphics_graph_item.py @@ -118,13 +118,13 @@ class GraphicsGraphItem(GraphicsGraphEvent, QGraphicsItemGroup): # PySide2 / for op_id, op_start_time in self.schedule.start_times.items(): op = self.schedule.sfg.find_by_id(op_id) - if not isinstance(op, (Input, Output)): - self._components_height += spacing - component = GraphicsComponentItem(op_id, op.latency_offsets, op.execution_time) - component.setPos(self._x_axis_indent + op_start_time, self._components_height) - self._components.append(component) - self._components_height += component.height - self._event_items += component.event_items +# if not isinstance(op, (Input, Output)): + self._components_height += spacing + component = GraphicsComponentItem(op) + component.setPos(self._x_axis_indent + op_start_time, self._components_height) + self._components.append(component) + self._components_height += component.height + self._event_items += component.event_items # self._components_height += spacing # build axes diff --git a/b_asic/scheduler-gui/main_window.py b/b_asic/scheduler-gui/main_window.py index 2b59c7ea..69959a4f 100644 --- a/b_asic/scheduler-gui/main_window.py +++ b/b_asic/scheduler-gui/main_window.py @@ -24,7 +24,6 @@ from qtpy.QtWidgets import ( # QGraphics and QPainter imports from qtpy.QtCore import QRectF, QByteArray -from qtpy.QtGui import QIcon from qtpy.QtWidgets import QGraphicsScene, QGraphicsItemGroup # B-ASIC diff --git a/b_asic/special_operations.py b/b_asic/special_operations.py index 88c72931..39d94330 100644 --- a/b_asic/special_operations.py +++ b/b_asic/special_operations.py @@ -19,6 +19,8 @@ class Input(AbstractOperation): Its value will be updated on each iteration when simulating the SFG. """ + _execution_time = 0 + def __init__(self, name: Name = ""): """Construct an Input operation.""" super().__init__(input_count=0, output_count=1, name=name, latency_offsets={'out0' : 0}) @@ -57,6 +59,8 @@ class Output(AbstractOperation): destinations. """ + _execution_time = 0 + def __init__(self, src0: Optional[SignalSourceProvider] = None, name: Name = ""): """Construct an Output operation.""" super().__init__(input_count=1, output_count=0, -- GitLab