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>&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>&Close Schedule</string> + </property> + </action> </widget> <resources> <include location="icons/basic.qrc"/> -- GitLab