From 912318a436efb0273d9c7887860d99f3f50e5c15 Mon Sep 17 00:00:00 2001
From: Oscar Gustafsson <oscar.gustafsson@gmail.com>
Date: Mon, 30 Jan 2023 14:57:38 +0100
Subject: [PATCH] Fix typing

---
 b_asic/operation.py                     |  6 ++++-
 b_asic/scheduler_gui/axes_item.py       |  4 ++--
 b_asic/scheduler_gui/main_window.py     | 32 ++++++++++++++++---------
 b_asic/scheduler_gui/operation_item.py  |  6 +++--
 b_asic/scheduler_gui/scheduler_event.py |  2 +-
 b_asic/scheduler_gui/scheduler_item.py  | 17 +++++++------
 6 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/b_asic/operation.py b/b_asic/operation.py
index aa84a0ed..863893af 100644
--- a/b_asic/operation.py
+++ b/b_asic/operation.py
@@ -397,6 +397,10 @@ class Operation(GraphComponent, SignalSourceProvider):
     def _decrease_time_resolution(self, factor: int) -> None:
         raise NotImplementedError
 
+    @abstractmethod
+    def _check_all_latencies_set(self) -> None:
+        raise NotImplementedError
+
 
 class AbstractOperation(Operation, AbstractGraphComponent):
     """
@@ -987,7 +991,7 @@ class AbstractOperation(Operation, AbstractGraphComponent):
         ]
 
     def _check_all_latencies_set(self):
-        if any(val is None for _, val in self.latency_offsets.items()):
+        if any(val is None for val in self.latency_offsets.values()):
             raise ValueError(
                 f"All latencies must be set: {self.latency_offsets}"
             )
diff --git a/b_asic/scheduler_gui/axes_item.py b/b_asic/scheduler_gui/axes_item.py
index 39bb40a0..4ac563d1 100644
--- a/b_asic/scheduler_gui/axes_item.py
+++ b/b_asic/scheduler_gui/axes_item.py
@@ -49,8 +49,8 @@ class AxesItem(QGraphicsItemGroup):
 
     def __init__(
         self,
-        width: int,
-        height: int,
+        width: float,
+        height: float,
         width_indent: float = 0.2,
         height_indent: float = 0.2,
         width_padding: float = 0.6,
diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py
index 1bd63565..dc5b086b 100644
--- a/b_asic/scheduler_gui/main_window.py
+++ b/b_asic/scheduler_gui/main_window.py
@@ -12,7 +12,7 @@ import os
 import sys
 from copy import deepcopy
 from importlib.machinery import SourceFileLoader
-from typing import Union
+from typing import Optional, Union, cast
 
 # Qt/qtpy
 import qtpy
@@ -43,7 +43,7 @@ from qtpy.QtWidgets import (
 
 # B-ASIC
 import b_asic.scheduler_gui.logger as logger
-from b_asic.graph_component import GraphComponent
+from b_asic.graph_component import GraphComponent, GraphID
 from b_asic.schedule import Schedule
 from b_asic.scheduler_gui.axes_item import AxesItem
 from b_asic.scheduler_gui.operation_item import OperationItem
@@ -64,7 +64,7 @@ if __debug__:
     # Print some system version information
     from qtpy import QtCore
 
-    QT_API = os.environ.get("QT_API")
+    QT_API = os.environ.get("QT_API", "")
     log.debug("Qt version (runtime):     {}".format(QtCore.qVersion()))
     log.debug("Qt version (compiletime): {}".format(QtCore.__version__))
     log.debug("QT_API:                   {}".format(QT_API))
@@ -153,7 +153,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         self._scene.sceneRectChanged.connect(self.shrink_scene_to_min_size)
 
     @property
-    def schedule(self) -> Schedule:
+    def schedule(self) -> Optional[Schedule]:
         """Get the current schedule."""
         return self._schedule
 
@@ -163,8 +163,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
     @Slot()
     def _actionTbtn(self) -> None:
         # TODO: remove
+        if self.schedule is None:
+            return
         self.schedule.plot_schedule()
-        print(f"filtersChildEvents(): {self._graph.filtersChildEvents()}")
+        if self._graph is not None:
+            print(f"filtersChildEvents(): {self._graph.filtersChildEvents()}")
         # self._printButtonPressed('callback_pushButton()')
 
     @Slot()
@@ -342,7 +345,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
             self._splitter_pos = width
 
     @Slot(str)
-    def info_table_update_component(self, op_id: str) -> None:
+    def info_table_update_component(self, op_id: GraphID) -> None:
         """
         SLOT(str) for SIGNAL(_graph._signals.component_selected)
         Takes in an operator-id, first clears the 'Operator' part of the info
@@ -358,7 +361,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         SLOT() for SIGNAL(_graph._signals.schedule_time_changed)
         Updates the 'Schedule' part of the info table.
         """
-        self.info_table.item(1, 1).setText(str(self.schedule.schedule_time))
+        if self.schedule is not None:
+            self.info_table.item(1, 1).setText(
+                str(self.schedule.schedule_time)
+            )
 
     @Slot(QRectF)
     def shrink_scene_to_min_size(self, rect: QRectF) -> None:
@@ -425,7 +431,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         """Take a Schedule and create a SchedulerItem object."""
         self.close_schedule()
         self._schedule = deepcopy(schedule)
-        self._graph = SchedulerItem(self.schedule)
+        self._graph = SchedulerItem(self._schedule)
         self._graph.setPos(1 / self._scale, 1 / self._scale)
         self.menu_close_schedule.setEnabled(True)
         self._scene.addItem(self._graph)
@@ -436,7 +442,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         self._graph._signals.schedule_time_changed.connect(
             self.info_table_update_schedule
         )
-        self.info_table_fill_schedule(self.schedule)
+        self.info_table_fill_schedule(self._schedule)
         self.update_statusbar(self.tr("Schedule loaded successfully"))
 
     def update_statusbar(self, msg: str) -> None:
@@ -502,12 +508,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         )
         self.info_table.setItem(2, 1, QTableWidgetItem(str(schedule.cyclic)))
 
-    def _info_table_fill_component(self, op_id: str) -> None:
+    def _info_table_fill_component(self, op_id: GraphID) -> None:
         """
         Take an operator-id and fill in the 'Operator' part of the info
         table with values from the operator associated with *op_id*.
         """
-        op: GraphComponent = self.schedule.sfg.find_by_id(op_id)
+        if self.schedule is None:
+            return
+        op: GraphComponent = cast(
+            GraphComponent, self.schedule.sfg.find_by_id(op_id)
+        )
         si = self.info_table.rowCount()  # si = start index
 
         if op.graph_id:
diff --git a/b_asic/scheduler_gui/operation_item.py b/b_asic/scheduler_gui/operation_item.py
index d11205d9..39c80e18 100644
--- a/b_asic/scheduler_gui/operation_item.py
+++ b/b_asic/scheduler_gui/operation_item.py
@@ -20,6 +20,7 @@ from qtpy.QtWidgets import (
 )
 
 # B-ASIC
+from b_asic.graph_component import GraphID
 from b_asic.operation import Operation
 from b_asic.scheduler_gui._preferences import (
     OPERATION_EXECUTION_TIME_INACTIVE,
@@ -56,8 +57,9 @@ class OperationItem(QGraphicsItemGroup):
         super().__init__(parent=parent)
         self._operation = operation
         self._height = height
+        operation._check_all_latencies_set()
         self._ports = {
-            k: {"latency": float(v)}
+            k: {"latency": float(v) if v is not None else None}
             for k, v in operation.latency_offsets.items()
         }
         self._end_time = max(operation.latency_offsets.values())
@@ -88,7 +90,7 @@ class OperationItem(QGraphicsItemGroup):
             del item
 
     @property
-    def op_id(self) -> str:
+    def op_id(self) -> GraphID:
         """Get the op-id."""
         return self._operation.graph_id
 
diff --git a/b_asic/scheduler_gui/scheduler_event.py b/b_asic/scheduler_gui/scheduler_event.py
index 67fe9527..0f52e3e8 100644
--- a/b_asic/scheduler_gui/scheduler_event.py
+++ b/b_asic/scheduler_gui/scheduler_event.py
@@ -35,7 +35,7 @@ class SchedulerEvent:  # PyQt5
         component_selected = Signal(str)
         schedule_time_changed = Signal()
 
-    _axes: AxesItem
+    _axes: Optional[AxesItem]
     _current_pos: QPointF
     _delta_time: int
     _signals: Signals  # PyQt5
diff --git a/b_asic/scheduler_gui/scheduler_item.py b/b_asic/scheduler_gui/scheduler_item.py
index fc56da5a..a28b4c59 100644
--- a/b_asic/scheduler_gui/scheduler_item.py
+++ b/b_asic/scheduler_gui/scheduler_item.py
@@ -8,12 +8,14 @@ maintain a component in a graph.
 from collections import defaultdict
 from math import floor
 from pprint import pprint
-from typing import Dict, List, Optional, Set
+from typing import Dict, List, Optional, Set, cast
 
 # QGraphics and QPainter imports
 from qtpy.QtWidgets import QGraphicsItem, QGraphicsItemGroup
 
 # B-ASIC
+from b_asic.operation import Operation
+from b_asic.port import InputPort
 from b_asic.schedule import Schedule
 from b_asic.scheduler_gui.axes_item import AxesItem
 from b_asic.scheduler_gui.operation_item import OperationItem
@@ -29,7 +31,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup):  # PySide2 / PyQt5
     also inherits from SchedulerEvent, which acts as a filter for events
     to OperationItem objects."""
     _schedule: Schedule
-    _axes: AxesItem
+    _axes: Optional[AxesItem]
     _components: List[OperationItem]
     _components_height: float
     _x_axis_indent: float
@@ -129,6 +131,8 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup):  # PySide2 / PyQt5
 
     def set_schedule_time(self, delta_time: int) -> None:
         """Set the schedule time and redraw the graph."""
+        if self._axes is None:
+            raise RuntimeError("No AxesItem!")
         assert self.schedule is not None, "No schedule installed."
         self.schedule.set_schedule_time(
             self.schedule.schedule_time + delta_time
@@ -145,7 +149,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup):  # PySide2 / PyQt5
         return self._schedule
 
     @property
-    def axes(self) -> AxesItem:
+    def axes(self) -> Optional[AxesItem]:
         return self._axes
 
     @property
@@ -164,7 +168,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup):  # PySide2 / PyQt5
         _components_dict = {}
         # print('Start times:')
         for op_id, op_start_time in self.schedule.start_times.items():
-            operation = self.schedule.sfg.find_by_id(op_id)
+            operation = cast(Operation, self.schedule.sfg.find_by_id(op_id))
 
             #            if not isinstance(op, (Input, Output)):
             self._components_height += spacing
@@ -196,9 +200,8 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup):  # PySide2 / PyQt5
         for component in self._components:
             for output_port in component.operation.outputs:
                 for signal in output_port.signals:
-                    dest_component = _components_dict[
-                        signal.destination.operation
-                    ]
+                    destination = cast(InputPort, signal.destination)
+                    dest_component = _components_dict[destination.operation]
                     gui_signal = SignalItem(
                         component, dest_component, signal, parent=self
                     )
-- 
GitLab