From 6beade66cc7d55045062065ec7fe1fd7009022f9 Mon Sep 17 00:00:00 2001
From: Andreas Bolin <2535580+andbo467@users.noreply.github.com>
Date: Tue, 2 Aug 2022 17:10:36 +0200
Subject: [PATCH] workspace dump

---
 .../scheduler-gui/graphics_component_item.py  |  19 +-
 b_asic/scheduler-gui/graphics_graph_event.py  |  23 +-
 b_asic/scheduler-gui/graphics_graph_item.py   |  27 ++-
 b_asic/scheduler-gui/main_window.py           | 208 ++++++++----------
 b_asic/scheduler-gui/main_window.ui           |  17 +-
 5 files changed, 149 insertions(+), 145 deletions(-)

diff --git a/b_asic/scheduler-gui/graphics_component_item.py b/b_asic/scheduler-gui/graphics_component_item.py
index c2b8e787..bf04a5a4 100644
--- a/b_asic/scheduler-gui/graphics_component_item.py
+++ b/b_asic/scheduler-gui/graphics_component_item.py
@@ -47,6 +47,7 @@ class GraphicsComponentItem(QGraphicsItemGroup):
     _height:                float
     _ports:                 Dict[str, Dict[str, Union[float, QPointF]]]     # ['port-id']['latency/pos']
     _execution_time:        Union[int, None]
+    _end_time:              int
     _component_item:        QGraphicsPathItem
     _execution_time_item:   QGraphicsRectItem
     _label_item:            QGraphicsSimpleTextItem
@@ -59,6 +60,9 @@ class GraphicsComponentItem(QGraphicsItemGroup):
         self._op_id = op_id
         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._port_items = []
 
@@ -70,11 +74,11 @@ class GraphicsComponentItem(QGraphicsItemGroup):
         
         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 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:
@@ -100,6 +104,11 @@ class GraphicsComponentItem(QGraphicsItemGroup):
             self.clear()
             self._height = height
             self._make_component()
+    
+    @property
+    def end_time(self) -> int:
+        """Get the relative end time."""
+        return self._end_time
             
     
     @property
diff --git a/b_asic/scheduler-gui/graphics_graph_event.py b/b_asic/scheduler-gui/graphics_graph_event.py
index ab354462..f9b7b9a4 100644
--- a/b_asic/scheduler-gui/graphics_graph_event.py
+++ b/b_asic/scheduler-gui/graphics_graph_event.py
@@ -22,7 +22,7 @@ from qtpy import QtWidgets
 
 # QGraphics and QPainter imports
 from qtpy.QtCore    import (
-    Qt, QObject, QRect, QRectF, QPoint, QSize, QSizeF, QByteArray, Slot, QEvent)
+    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)
@@ -45,12 +45,23 @@ from graphics_timeline_item     import GraphicsTimelineItem
 
 
 # class GraphicsGraphEvent(ABC):
-class GraphicsGraphEvent(QGraphicsItem):
+class GraphicsGraphEvent(QGraphicsObject, QGraphicsItem):
     """Event filter and handlers for GraphicsGraphItem"""
     _axes:          GraphicsAxesItem
     _current_pos:   QPointF
     _delta_time:    int
+    
+    component_selected = Signal(str)
+    
+    @overload
+    def is_component_valid_pos(self, pos: float, end_time: int) -> bool: ...
+    @overload
+    def is_valid_delta_time(self, delta_time: int) -> bool: ...
+    @overload
+    def set_schedule_time(self, delta_time: int) -> None: ...
 
+    def __init__(self):
+        super().__init__()
 
     #################
     #### Filters ####
@@ -158,17 +169,17 @@ class GraphicsGraphEvent(QGraphicsItem):
         horizontally in x-axis scale steps."""
         # Qt.DragMoveCursor
         # button = event.button()
-        item = self.scene().mouseGrabberItem()
+        item: GraphicsComponentItem = self.scene().mouseGrabberItem()
         dx = (item.mapToParent(event.pos()) - self._current_pos).x()
         if dx > 0.505:
             pos = item.x() + 1.0
-            if self.is_component_valid_pos(pos):
+            if self.is_component_valid_pos(pos, item.end_time):
                 # self.prepareGeometryChange()
                 item.setX(pos)
                 self._current_pos.setX(self._current_pos.x() + 1.0)
         elif dx < -0.505:
             pos = item.x() - 1.0
-            if self.is_component_valid_pos(pos):
+            if self.is_component_valid_pos(pos, item.end_time):
                 # self.prepareGeometryChange()
                 item.setX(pos)
                 self._current_pos.setX(self._current_pos.x() - 1.0)
@@ -179,8 +190,10 @@ class GraphicsGraphEvent(QGraphicsItem):
         by default be accepted, and this item is then the mouse grabber. This
         allows the item to receive future move, release and double-click events."""
         item: GraphicsComponentItem = self.scene().mouseGrabberItem()
+        self.component_selected.emit(item.op_id)
         # op = self.schedule.sfg.find_by_id(item.op_id)
         # emit fill_info_table_component(op)
+        
         self._current_pos = item.mapToParent(event.pos())
         self.setCursor(QCursor(Qt.ClosedHandCursor))
         event.accept()
diff --git a/b_asic/scheduler-gui/graphics_graph_item.py b/b_asic/scheduler-gui/graphics_graph_item.py
index eead2956..147cf984 100644
--- a/b_asic/scheduler-gui/graphics_graph_item.py
+++ b/b_asic/scheduler-gui/graphics_graph_item.py
@@ -5,6 +5,7 @@
 Contains the scheduler-gui GraphicsGraphItem class for drawing and
 maintain a component in a graph.
 """
+from math import floor
 import os
 import sys
 from typing     import Any, Optional
@@ -79,18 +80,26 @@ class GraphicsGraphItem(QGraphicsItemGroup, GraphicsGraphEvent):
             item.setParentItem(None)
             del item
     
-    def is_component_valid_pos(self, pos: float) -> bool:
+    def is_component_valid_pos(self, pos: float, end_time: int) -> bool:
         """Takes in a component position and returns true if the component's new
         position is valid, false otherwise."""
         # TODO: implement
-        # item = self.scene().mouseGrabberItem()
+        assert self.schedule is not None , "No schedule installed."
+        start_time = floor(pos) - floor(self._x_axis_indent)
         if pos < 0:
             return False
+        elif (self.schedule.cyclic
+              and start_time > self.schedule.schedule_time):
+            return False
+        elif (not self.schedule.cyclic
+              and start_time + end_time > self.schedule.schedule_time):
+            return False
+            
         return True
 
     def is_valid_delta_time(self, delta_time: int) -> bool:
-        """Takes in a delta time and returns true if the new schedule time is valid
-        , false otherwise."""
+        """Takes in a delta time and returns true if the new schedule time is
+        valid, false otherwise."""
         # TODO: implement
         # item = self.scene().mouseGrabberItem()
         assert self.schedule is not None , "No schedule installed."
@@ -101,17 +110,7 @@ class GraphicsGraphItem(QGraphicsItemGroup, GraphicsGraphEvent):
         """Set the schedule time and redraw the graph."""
         assert self.schedule is not None , "No schedule installed."
         self.schedule.set_schedule_time(self.schedule.schedule_time + delta_time)
-        # scene = self.scene()
-        # if scene is not None:
-        #     self.removeSceneEventFilters(self._axes.event_items)
-        # self._axes.update_axes(width = self._axes.width + delta_time)
-        # if scene is not None:
-        #     self.installSceneEventFilters(self._axes.event_items)
-            # print(f'self._axes.event_items {self._axes.event_items}')
-        # print(f'set_schedule_time({delta_time})')
-        
         self._axes.set_width(self._axes.width + delta_time)
-        # self.scene().views()[0].updateScene(QRectF(0, 0, 1, 1))
         
         
     @property
diff --git a/b_asic/scheduler-gui/main_window.py b/b_asic/scheduler-gui/main_window.py
index 95db2806..df69b0d6 100644
--- a/b_asic/scheduler-gui/main_window.py
+++ b/b_asic/scheduler-gui/main_window.py
@@ -10,7 +10,7 @@ import os
 import sys
 from pathlib        import Path
 from types          import ModuleType
-from typing         import Any, Iterable, List, Sequence, Type, Dict
+from typing         import Any, Iterable, List, Sequence, Type, Dict, Union
 from pprint         import pprint
 #from matplotlib.pyplot import bar
 #from diagram import *
@@ -113,12 +113,11 @@ QCoreApplication.setApplicationName('B-ASIC Scheduler')
 class MainWindow(QMainWindow, Ui_MainWindow):
     """Schedule of an SFG with scheduled Operations."""
     _scene: QGraphicsScene
-    _graph: GraphicsGraphItem
+    _graph: Union[GraphicsGraphItem, None]
     _scale: float
     _debug_rects: QGraphicsItemGroup
     _splitter_pos: int
     _splitter_min: int
-    _table_items: Dict[str, QTableWidgetItem]
 
     
     def __init__(self):
@@ -128,14 +127,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         self._open_file_dialog_opened = False
         self._scale = 75
         self._debug_rects = None
-        self._table_items: Dict[str, QTableWidgetItem]= {
-            'schedule_time':    QTableWidgetItem(), # Schedule related part
-            'cyclic':           QTableWidgetItem(),
-            'resolution':       QTableWidgetItem(),
-            'id':               QTableWidgetItem(), # Component realtaed part
-            'name':             QTableWidgetItem(),
-            'inports':          QTableWidgetItem(),
-            'outports':         QTableWidgetItem()}
         
         QIcon.setThemeName('breeze')
         log.debug('themeName: \'{}\''.format(QIcon.themeName()))
@@ -154,6 +145,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         
         # Connect signals to slots
         self.menu_load_from_file.triggered      .connect(self._load_schedule_from_pyfile)
+        self.menu_close_schedule.triggered      .connect(self.close_schedule)
         self.menu_save          .triggered      .connect(self.save)
         self.menu_save_as       .triggered      .connect(self.save_as)
         self.menu_quit          .triggered      .connect(self.close)
@@ -191,84 +183,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
     def _init_graphics(self) -> None:
         """Initialize the QGraphics framework"""
         self._scene = QGraphicsScene()
+        self._scene.addRect(0, 0, 0, 0)             # dummy rect to be able to setPos graph
         self.view.setScene(self._scene)
         self.view.scale(self._scale, self._scale)
         GraphicsComponentItem._scale = self._scale
         GraphicsAxesItem._scale = self._scale
         self._scene.changed.connect(self.shrink_scene_to_min_size)
 
-    def fill_info_table_schedule(self, schedule: Schedule) -> None:
-        self._table_items['schedule_time'].setText(str(schedule.schedule_time))
-        self._table_items['cyclic'].setText(str(schedule.cyclic))
-        self._table_items['resolution'].setText(str(schedule.resolution))
-        self.info_table.insertRow(1)
-        self.info_table.insertRow(1)
-        self.info_table.insertRow(1)
-        self.info_table.setItem(1, 0, QTableWidgetItem('Schedule Time'))
-        self.info_table.setItem(2, 0, QTableWidgetItem('Cyclic'))
-        self.info_table.setItem(3, 0, QTableWidgetItem('Resolution'))
-        self.info_table.setItem(1, 1 ,self._table_items['schedule_time'])
-        self.info_table.setItem(2, 1 ,self._table_items['cyclic'])
-        self.info_table.setItem(3, 1 ,self._table_items['resolution'])
-        # self.info_table.setVerticalHeaderItem(0, mydict['test'])
-        # self._table_items['schedule_time'].setText('test item updated text')
-        
-        for i in range(5,10):
-            self.info_table.insertRow(i)
-            item = QTableWidgetItem('this is a very very very very long string that says abolutly nothing')
-            self.info_table.setItem(i,0, QTableWidgetItem('property {}: '.format(i)))
-            self.info_table.setItem(i,1,item)
-
-    def fill_info_table_component(self, op: GraphComponent) -> None:
-        si = len(self._table_items) + 1         # si = start index
-        self.info_table.insertRow(si)
-        self.info_table.insertRow(si)
-        self.info_table.setItem(si + 0, 0, QTableWidgetItem('Graph ID'))
-        self.info_table.setItem(si + 0, 1, QTableWidgetItem(str(op.graph_id)))
-        self.info_table.setItem(si + 1, 0, QTableWidgetItem('Name'))
-        self.info_table.setItem(si + 1, 1, QTableWidgetItem(str(op.name)))
-        
-        si += 2
-        params = [(k,v) for k,v in op.params]
-        for i in range(len(params)):
-            self.info_table.insertRow(si + i)
-            self.info_table.setItem(si + i, 0, QTableWidgetItem(params[i][0]))
-            self.info_table.setItem(si + i, 1, QTableWidgetItem(params[i][1]))
-
-
-        # graph_id
-        # name
-        # params (dict)
-        self._table_items['schedule_time'].setText(str(schedule.schedule_time))
-        self._table_items['cyclic'].setText(str(schedule.cyclic))
-        self._table_items['resolution'].setText(str(schedule.resolution))
-        self.info_table.insertRow(si)
-        self.info_table.insertRow(si)
-        self.info_table.insertRow(si)
-        self.info_table.setItem(si + 0, 0, QTableWidgetItem('ID'))
-        self.info_table.setItem(si + 1, 0, QTableWidgetItem('Name'))
-        self.info_table.setItem(si + 3, 0, QTableWidgetItem('Inports'))
-        self.info_table.setItem(si + 3, 0, QTableWidgetItem('Outports'))
-        self.info_table.setItem(si + 1, 1 ,self._table_items['id'])
-        self.info_table.setItem(si + 2, 1 ,self._table_items['name'])
-        self.info_table.setItem(si + 3, 1 ,self._table_items['inports'])
-        self.info_table.setItem(si + 3, 1 ,self._table_items['outports'])
-        # self.info_table.setVerticalHeaderItem(0, mydict['test'])
-        # self._table_items['schedule_time'].setText('test item updated text')
-        
-        for i in range(5,10):
-            self.info_table.insertRow(i)
-            item = QTableWidgetItem('this is a very very very very long string that says abolutly nothing')
-            self.info_table.setItem(i,0, QTableWidgetItem('property {}: '.format(i)))
-            self.info_table.setItem(i,1,item)
-    
-    def clear_info_table_schedule(self) -> None:
-        for _ in range(3):                  # Remove Schedule info
-            self.info_table.removeRow(1)
-    
-    def clear_info_table_component(self) -> None:
-        for _ in range(5):                # Remove component info
-            self.info_table.removeRow(2)
+   
 
     
 
@@ -339,43 +261,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         del module
         settings.setValue("mainwindow/last_opened_file", abs_path_filename)
 
+    @Slot()
+    def close_schedule(self) -> None:
+        self._graph.removeSceneEventFilters(self._graph.event_items)
+        self._scene.removeItem(self._graph)
+        self.menu_close_schedule.setEnabled(False)
+        del self._graph
+        self._graph = None
+        self.clear_info_table()
         
-    
-    #@Slot()
-    def open(self, schedule: Schedule) -> None:
-        """Takes in an Schedule and creates a GraphicsGraphItem object."""
-
-        self._graph = GraphicsGraphItem(schedule)
-        self._scene.addItem(self._graph)
-        self._graph.installSceneEventFilters(self._graph.event_items)
-
-        
-        # graph.prepareGeometryChange()
-        # graph.setPos(200, 20)
-
-        # self._scene.setSceneRect(self._scene.itemsBoundingRect())   # Forces the scene to it's minimum size
-        
-        # # Debug rectangles
-        # if __debug__:
-        #     # self._scene.setSceneRect(self._scene.itemsBoundingRect())   # Forces the scene to it's minimum size
-        #     m = QMarginsF(1/self._scale, 1/self._scale, 0, 0)
-        #     m2 = QMarginsF(1, 1, 1, 1)
-        #     pen = QPen(Qt.red)
-        #     pen.setStyle(Qt.DotLine)
-        #     pen.setCosmetic(True)
-        #     for component in graph.items:
-        #         self._scene.addRect(component.mapRectToScene(component.boundingRect() - m), pen)
-        #     pen.setColor(Qt.red)
-        #     for axis in graph.axes.childItems():
-        #         self._scene.addRect(axis.mapRectToScene(axes.boundingRect() - m), pen)
-        #     pen.setColor(Qt.green)
-        #     # self._scene.addRect(self._scene.itemsBoundingRect() - m, pen)
-            
-        # self._scene.setSceneRect(self._scene.itemsBoundingRect())
-
-        self.update_statusbar(self.tr('Schedule loaded successfully'))
-        self.fill_info_table_schedule(self._graph.schedule)
-    
     @Slot()
     def save(self) -> None:
         """This method save an schedule."""
@@ -424,7 +318,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
                 self.menu_node_info.setChecked(True)
             self._splitter_pos = width
 
-        
+    @Slot(str)
+    def fill_info_table_component(self, op_id: str) -> None:
+        """Taked in an operator-id, first clears the 'Operator' part of the info
+        table and then fill in the table with new values from the operator
+        associated with 'op_id'."""
+        self.clear_info_table_component()
+        self._fill_info_table_component(op_id)
+    
     @Slot('QList<QRectF>')
     def shrink_scene_to_min_size(self, region: List[QRectF]) -> None:
         self._scene.setSceneRect(self._scene.itemsBoundingRect())
@@ -475,6 +376,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         alert = QMessageBox(self)
         alert.setText("Called from " + func_name + '!')
         alert.exec_()
+    
+    def open(self, schedule: Schedule) -> None:
+        """Takes in an Schedule and creates a GraphicsGraphItem object."""
+        if self._graph:
+            print('Graph opened!')
+            self.close_schedule()
+        self._graph = GraphicsGraphItem(schedule)
+        self._graph.setPos(1/self._scale, 1/self._scale)
+        self.menu_close_schedule.setEnabled(True)
+        self._scene.addItem(self._graph)
+        self._graph.installSceneEventFilters(self._graph.event_items)
+        self._graph.component_selected.connect(self.fill_info_table_component)
+        self.fill_info_table_schedule(self._graph.schedule)
+        self.update_statusbar(self.tr('Schedule loaded successfully'))
 
     def update_statusbar(self, msg: str) -> None:
         """Write the given str to the statusbar with temporarily policy."""
@@ -511,6 +426,59 @@ class MainWindow(QMainWindow, Ui_MainWindow):
         self.menu_exit_dialog.setChecked(   s.value('mainwindow/hide_exit_dialog', False, bool))
 
         log.debug('Settings read from \'{}\'.'.format(s.fileName()))
+    
+    def fill_info_table_schedule(self, schedule: Schedule) -> None:
+        """Takes in a Schedule and fill in the 'Schedule' part of the info table
+        with values from 'schedule'"""
+        self.info_table.insertRow(1)
+        self.info_table.insertRow(1)
+        self.info_table.insertRow(1)
+        self.info_table.setItem(1, 0, QTableWidgetItem('Schedule Time'))
+        self.info_table.setItem(2, 0, QTableWidgetItem('Cyclic'))
+        self.info_table.setItem(3, 0, QTableWidgetItem('Resolution'))
+        self.info_table.setItem(1, 1, QTableWidgetItem(str(schedule.schedule_time)))
+        self.info_table.setItem(2, 1, QTableWidgetItem(str(schedule.cyclic)))
+        self.info_table.setItem(3, 1, QTableWidgetItem(str(schedule.resolution)))
+
+    def _fill_info_table_component(self, op_id: str) -> None:
+        """Taked in 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._graph.schedule.sfg.find_by_id(op_id)
+        si = self.info_table.rowCount()                                         # si = start index
+
+        if op.graph_id:
+            self.info_table.insertRow(si)
+            self.info_table.setItem(si, 0, QTableWidgetItem('ID'))
+            self.info_table.setItem(si, 1, QTableWidgetItem(str(op.graph_id)))
+            si += 1
+        if op.name:
+            self.info_table.insertRow(si)
+            self.info_table.setItem(si, 0, QTableWidgetItem('Name'))
+            self.info_table.setItem(si, 1, QTableWidgetItem(str(op.name)))
+            si += 1
+        
+        # params: dict = op.params
+        print('Params:')
+        pprint(op.params)
+        for key, value in op.params:
+            self.info_table.insertRow(si)
+            self.info_table.setItem(si, 0, QTableWidgetItem(key))
+            self.info_table.setItem(si, 1, QTableWidgetItem(str(value)))
+            si += 1
+            
+    
+    def clear_info_table(self) -> None:
+        """Clears the info table."""
+        self.clear_info_table_component()
+        if self.info_table.rowCount() > 2:
+            for _ in range(3):
+                self.info_table.removeRow(1)
+    
+    def clear_info_table_component(self) -> None:
+        """Clears only the component part of the info table, assuming that the table is filled."""
+        if self.info_table.rowCount() > 5:
+            for _ in range(self.info_table.rowCount() - 5):
+                self.info_table.removeRow(2)
 
 
 
diff --git a/b_asic/scheduler-gui/main_window.ui b/b_asic/scheduler-gui/main_window.ui
index 99e0ed0e..c11f5929 100644
--- a/b_asic/scheduler-gui/main_window.ui
+++ b/b_asic/scheduler-gui/main_window.ui
@@ -106,7 +106,7 @@
      <x>0</x>
      <y>0</y>
      <width>800</width>
-     <height>22</height>
+     <height>20</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">
@@ -114,6 +114,7 @@
      <string>&amp;File</string>
     </property>
     <addaction name="menu_load_from_file"/>
+    <addaction name="menu_close_schedule"/>
     <addaction name="menu_save"/>
     <addaction name="menu_save_as"/>
     <addaction name="separator"/>
@@ -178,6 +179,9 @@
    </property>
   </action>
   <action name="menu_save">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
    <property name="icon">
     <iconset theme="document-save">
      <normaloff>.</normaloff>.</iconset>
@@ -230,6 +234,9 @@
    </property>
   </action>
   <action name="menu_save_as">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
    <property name="icon">
     <iconset theme="document-save-as">
      <normaloff>.</normaloff>.</iconset>
@@ -254,6 +261,14 @@
     <string>T</string>
    </property>
   </action>
+  <action name="menu_close_schedule">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>&amp;Close Schedule</string>
+   </property>
+  </action>
  </widget>
  <resources>
   <include location="icons/basic.qrc"/>
-- 
GitLab