From fa15c7a435b196f15a956ddb82b2ff930d2a24b3 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson <oscar.gustafsson@gmail.com> Date: Mon, 20 Feb 2023 11:00:50 +0100 Subject: [PATCH] Fix documentation for scheduler GUI --- b_asic/scheduler_gui/axes_item.py | 54 ++++++--------- b_asic/scheduler_gui/compile.py | 59 ++++++++++++---- b_asic/scheduler_gui/logger.py | 7 +- b_asic/scheduler_gui/main_window.py | 91 +++++++++---------------- b_asic/scheduler_gui/operation_item.py | 41 +++++------ b_asic/scheduler_gui/scheduler_event.py | 76 ++++++--------------- b_asic/scheduler_gui/scheduler_item.py | 34 ++++----- b_asic/scheduler_gui/signal_item.py | 17 +++-- b_asic/scheduler_gui/timeline_item.py | 10 ++- 9 files changed, 173 insertions(+), 216 deletions(-) diff --git a/b_asic/scheduler_gui/axes_item.py b/b_asic/scheduler_gui/axes_item.py index 9d5216cf..d11b7a7c 100644 --- a/b_asic/scheduler_gui/axes_item.py +++ b/b_asic/scheduler_gui/axes_item.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""B-ASIC Scheduler-gui Graphics Axes Item Module. +""" +B-ASIC Scheduler-gui Axes Item Module. -Contains the scheduler-gui AxesItem class for drawing and maintain the +Contains the scheduler-gui AxesItem class for drawing and maintaining the axes in a graph. """ from math import pi, sin @@ -25,18 +26,19 @@ from b_asic.scheduler_gui.timeline_item import TimelineItem class AxesItem(QGraphicsItemGroup): - """ + f""" A class to represent axes in a graph. Parameters ---------- width height - width_indent - height_indent - width_padding - height_padding - parent + width_indent : float, default: {SCHEDULE_INDENT} + height_indent : float, default: {SCHEDULE_INDENT} + width_padding : float, default: 0.6 + height_padding : float, default: 0.5 + parent : QGraphicsItem, optional + Passed to QGraphicsItemGroup. """ _scale: float = 1.0 @@ -72,17 +74,14 @@ class AxesItem(QGraphicsItemGroup): ): """ Class for an AxesItem. + *parent* is passed to QGraphicsItemGroup's constructor. """ super().__init__(parent=parent) if width < 0: - raise ValueError( - f"'width' greater or equal to 0 expected, got: {width}." - ) + raise ValueError(f"'width' greater or equal to 0 expected, got: {width}.") if height < 0: - raise ValueError( - f"'height' greater or equal to 0 expected, got: {height}." - ) + raise ValueError(f"'height' greater or equal to 0 expected, got: {height}.") self._width = width self._height = height @@ -170,18 +169,14 @@ class AxesItem(QGraphicsItemGroup): def set_height(self, height: float) -> None: # TODO: docstring if height < 0: - raise ValueError( - f"'height' greater or equal to 0 expected, got: {height}." - ) + raise ValueError(f"'height' greater or equal to 0 expected, got: {height}.") self._height = height self._update_yaxis() def set_width(self, width: int) -> None: # TODO: docstring if width < 0: - raise ValueError( - f"'width' greater or equal to 0 expected, got: {width}." - ) + raise ValueError(f"'width' greater or equal to 0 expected, got: {width}.") delta_width = width - self._width @@ -293,12 +288,7 @@ class AxesItem(QGraphicsItemGroup): 0, 0, 0, - -( - self._height_indent - + self._height - + self._height_padding - + 0.05 - ), + -(self._height_indent + self._height + self._height_padding + 0.05), ) self._y_axis.setPen(self._base_pen) @@ -325,15 +315,11 @@ class AxesItem(QGraphicsItemGroup): self._x_label.setScale(1 / self._scale) x_pos = self._width_indent + 0 + self._width_padding # end of x-axis x_pos += ( - self.mapRectFromItem( - self._x_arrow, self._x_arrow.boundingRect() - ).width() + self.mapRectFromItem(self._x_arrow, self._x_arrow.boundingRect()).width() / 2 ) # + half arrow width x_pos -= ( - self.mapRectFromItem( - self._x_label, self._x_label.boundingRect() - ).width() + self.mapRectFromItem(self._x_label, self._x_label.boundingRect()).width() / 2 ) # - center of label self._x_label.setPos(x_pos, self._x_label_offset) @@ -344,9 +330,7 @@ class AxesItem(QGraphicsItemGroup): for _ in range(self._width): self._append_x_tick() pos = self._x_ledger[-1].pos() - self._x_ledger[-1].setPos( - pos + QPointF(self._width, 0) - ) # move timeline + self._x_ledger[-1].setPos(pos + QPointF(self._width, 0)) # move timeline # y-axis self._update_yaxis() diff --git a/b_asic/scheduler_gui/compile.py b/b_asic/scheduler_gui/compile.py index 227828aa..388c884c 100644 --- a/b_asic/scheduler_gui/compile.py +++ b/b_asic/scheduler_gui/compile.py @@ -12,6 +12,7 @@ import os import shutil import subprocess import sys +from pathlib import Path from qtpy import uic from setuptools_scm import get_version @@ -31,31 +32,34 @@ def _check_filenames(*filenames: str) -> None: exception. """ for filename in filenames: - if not os.path.exists(filename): - raise FileNotFoundError(filename) + Path(filename).resolve(strict=True) def _check_qt_version() -> None: """ - Check if PySide2 or PyQt5 is installed, otherwise raise AssertionError + Check if PySide2, PyQt5, PySide6, or PyQt6 is installed, otherwise raise AssertionError exception. """ - assert uic.PYSIDE2 or uic.PYQT5, "PySide2 or PyQt5 need to be installed" + assert ( + uic.PYSIDE2 or uic.PYQT5 or uic.PYSIDE6 or uic.PYQT6 + ), "Python QT bindings must be installed" def replace_qt_bindings(filename: str) -> None: - """Raplaces qt-binding api in 'filename' from PySide2/PyQt5 to qtpy.""" + """Raplaces qt-binding api in *filename* from PySide2/6 or PyQt5/6 to qtpy.""" with open(f"{filename}", "r") as file: filedata = file.read() filedata = filedata.replace("from PyQt5", "from qtpy") filedata = filedata.replace("from PySide2", "from qtpy") + filedata = filedata.replace("from PyQt6", "from qtpy") + filedata = filedata.replace("from PySide6", "from qtpy") with open(f"{filename}", "w") as file: file.write(filedata) def compile_rc(*filenames: str) -> None: """ - Compile resource file(s) given by 'filenames'. If no arguments are given, + Compile resource file(s) given by *filenames*. If no arguments are given, the compiler will search for resource (.qrc) files and compile accordingly. """ _check_qt_version() @@ -70,10 +74,9 @@ def compile_rc(*filenames: str) -> None: if rcc is None: rcc = shutil.which("pyrcc5") arguments = f"-o {outfile} {filename}" - assert rcc, ( - "Qt Resource compiler failed, cannot find pyside2-rcc, rcc, or" - " pyrcc5" - ) + assert ( + rcc + ), "Qt Resource compiler failed, cannot find pyside2-rcc, rcc, or pyrcc5" os_ = sys.platform if os_.startswith("linux"): # Linux @@ -124,7 +127,7 @@ def compile_rc(*filenames: str) -> None: def compile_ui(*filenames: str) -> None: """ - Compile form file(s) given by 'filenames'. If no arguments are given, the + Compile form file(s) given by *filenames*. If no arguments are given, the compiler will search for form (.ui) files and compile accordingly. """ _check_qt_version() @@ -168,11 +171,43 @@ def compile_ui(*filenames: str) -> None: log.error(f"{os_} UI compiler not supported") raise NotImplementedError - else: # uic.PYQT5 + elif uic.PYQT5 or uic.PYQT6: from qtpy.uic import compileUi with open(outfile, "w") as ofile: compileUi(filename, ofile) + elif uic.PYQT6: + uic_ = shutil.which("pyside6-uic") + arguments = f"-g python -o {outfile} {filename}" + + if uic_ is None: + uic_ = shutil.which("uic") + if uic_ is None: + uic_ = shutil.which("pyuic6") + arguments = f"-o {outfile} {filename}" + assert uic_, ( + "Qt User Interface Compiler failed, cannot find pyside6-uic," + " uic, or pyuic6" + ) + + os_ = sys.platform + if os_.startswith("linux"): # Linux + cmd = f"{uic_} {arguments}" + subprocess.call(cmd.split()) + + elif os_.startswith("win32"): # Windows + # TODO: implement + log.error("Windows UI compiler not implemented") + raise NotImplementedError + + elif os_.startswith("darwin"): # macOS + # TODO: implement + log.error("macOS UI compiler not implemented") + raise NotImplementedError + + else: # other OS + log.error(f"{os_} UI compiler not supported") + raise NotImplementedError replace_qt_bindings(outfile) # replace qt-bindings with qtpy diff --git a/b_asic/scheduler_gui/logger.py b/b_asic/scheduler_gui/logger.py index 1746ae6c..b0038589 100644 --- a/b_asic/scheduler_gui/logger.py +++ b/b_asic/scheduler_gui/logger.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""B-ASIC Scheduler-gui Logger Module. +""" +B-ASIC Scheduler-gui Logger Module. Contains a logger that logs to the console and a file using levels. It is based on the :mod:`logging` module and has predefined levels of logging. @@ -55,9 +56,7 @@ from types import TracebackType from typing import Type, Union -def getLogger( - filename: str = "scheduler-gui.log", loglevel: str = "INFO" -) -> Logger: +def getLogger(filename: str = "scheduler-gui.log", loglevel: str = "INFO") -> Logger: """ This function creates console- and filehandler and from those, creates a logger object. diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index 97a70d96..8d56996e 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -B-ASIC Scheduler-gui Module. +B-ASIC Scheduler-GUI Module. -Contains the scheduler-gui MainWindow class for scheduling operations in an SFG. +Contains the scheduler_gui MainWindow class for scheduling operations in an SFG. -Start main-window with start_gui(). +Start main-window with ``start_gui()``. """ import inspect import os @@ -104,7 +104,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): _zoom: float def __init__(self): - """Initialize Scheduler-gui.""" + """Initialize Scheduler-GUI.""" super().__init__() self._schedule = None self._graph = None @@ -121,9 +121,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): """Initialize the ui""" # Connect signals to slots - self.menu_load_from_file.triggered.connect( - self._load_schedule_from_pyfile - ) + 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) @@ -153,9 +151,7 @@ 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._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) OperationItem._scale = self._scale @@ -182,12 +178,12 @@ class MainWindow(QMainWindow, Ui_MainWindow): @Slot() def _open_documentation(self) -> None: + """Callback to open documentation web page.""" webbrowser.open_new_tab("https://da.gitlab-pages.liu.se/B-ASIC/") @Slot() def _actionReorder(self) -> None: - """Callback to reorder all operations vertically based on start time. - """ + """Callback to reorder all operations vertically based on start time.""" if self.schedule is None: return if self._graph is not None: @@ -228,25 +224,17 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.tr("Python Files (*.py *.py3)"), ) - if ( - not abs_path_filename - ): # return if empty filename (QFileDialog was canceled) + if not abs_path_filename: # return if empty filename (QFileDialog was canceled) return log.debug("abs_path_filename = {}.".format(abs_path_filename)) module_name = inspect.getmodulename(abs_path_filename) if not module_name: # return if empty module name - log.error( - "Could not load module from file '{}'.".format( - abs_path_filename - ) - ) + log.error("Could not load module from file '{}'.".format(abs_path_filename)) return try: - module = SourceFileLoader( - module_name, abs_path_filename - ).load_module() + module = SourceFileLoader(module_name, abs_path_filename).load_module() except Exception as e: log.exception( "Exception occurred. Could not load module from file" @@ -262,9 +250,9 @@ class MainWindow(QMainWindow, Ui_MainWindow): QMessageBox.warning( self, self.tr("File not found"), - self.tr( - "Cannot find any Schedule object in file '{}'." - ).format(os.path.basename(abs_path_filename)), + self.tr("Cannot find any Schedule object in file '{}'.").format( + os.path.basename(abs_path_filename) + ), ) log.info( "Cannot find any Schedule object in file '{}'.".format( @@ -302,8 +290,9 @@ class MainWindow(QMainWindow, Ui_MainWindow): @Slot() def close_schedule(self) -> None: """ + Close current schedule. + SLOT() for SIGNAL(menu_close_schedule.triggered) - Closes current schedule. """ if self._graph: self._graph._signals.component_selected.disconnect( @@ -324,8 +313,9 @@ class MainWindow(QMainWindow, Ui_MainWindow): @Slot() def save(self) -> None: """ + Save current schedule. + SLOT() for SIGNAL(menu_save.triggered) - This method save a schedule. """ # TODO: all self._print_button_pressed("save_schedule()") @@ -334,19 +324,22 @@ class MainWindow(QMainWindow, Ui_MainWindow): @Slot() def save_as(self) -> None: """ + Save current schedule asking for file name. + SLOT() for SIGNAL(menu_save_as.triggered) - This method save as a schedule. """ - # TODO: all + # TODO: Implement self._print_button_pressed("save_schedule()") self.update_statusbar(self.tr("Schedule saved successfully")) @Slot(bool) def show_info_table(self, checked: bool) -> None: """ + Show or hide the info table. + SLOT(bool) for SIGNAL(menu_node_info.triggered) Takes in a boolean and hide or show the info table accordingly with - 'checked'. + *checked*. """ # Note: splitter handler index 0 is a hidden splitter handle far most left, # use index 1 @@ -406,9 +399,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): Updates the 'Schedule' part of the info table. """ if self.schedule is not None: - self.info_table.item(1, 1).setText( - str(self.schedule.schedule_time) - ) + self.info_table.item(1, 1).setText(str(self.schedule.schedule_time)) @Slot(QRectF) def shrink_scene_to_min_size(self, rect: QRectF) -> None: @@ -454,9 +445,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): if ret == QMessageBox.StandardButton.Yes: if not hide_dialog: - settings.setValue( - "scheduler/hide_exit_dialog", checkbox.isChecked() - ) + settings.setValue("scheduler/hide_exit_dialog", checkbox.isChecked()) self._write_settings() log.info("Exit: {}".format(os.path.basename(__file__))) event.accept() @@ -489,9 +478,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self._graph._signals.component_selected.connect( self.info_table_update_component ) - self._graph._signals.component_moved.connect( - self.info_table_update_component - ) + self._graph._signals.component_moved.connect(self.info_table_update_component) self._graph._signals.schedule_time_changed.connect( self.info_table_update_schedule ) @@ -520,12 +507,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): settings.setValue( "scheduler/state", self.saveState() ) # toolbars, dockwidgets: pos, size - settings.setValue( - "scheduler/menu/node_info", self.menu_node_info.isChecked() - ) - settings.setValue( - "scheduler/splitter/state", self.splitter.saveState() - ) + settings.setValue("scheduler/menu/node_info", self.menu_node_info.isChecked()) + settings.setValue("scheduler/splitter/state", self.splitter.saveState()) settings.setValue("scheduler/splitter/pos", self.splitter.sizes()[1]) if settings.isWritable(): @@ -536,9 +519,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): def _read_settings(self) -> None: """Read settings from Settings to MainWindow.""" settings = QSettings() - if settings.value( - "scheduler/maximized", defaultValue=False, type=bool - ): + if settings.value("scheduler/maximized", defaultValue=False, type=bool): self.showMaximized() else: self.move(settings.value("scheduler/pos", self.pos())) @@ -566,9 +547,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): 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( - 1, 1, QTableWidgetItem(str(schedule.schedule_time)) - ) + self.info_table.setItem(1, 1, QTableWidgetItem(str(schedule.schedule_time))) self.info_table.setItem(2, 1, QTableWidgetItem(str(schedule.cyclic))) def _info_table_fill_component(self, graph_id: GraphID) -> None: @@ -630,9 +609,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): for _ in range(3): self.info_table.removeRow(1) else: - log.error( - "'Operator' not found in info table. It may have been renamed." - ) + log.error("'Operator' not found in info table. It may have been renamed.") def exit_app(self) -> None: """Exit application.""" @@ -647,9 +624,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): for _ in range(self.info_table.rowCount() - row + 1): self.info_table.removeRow(row + 1) else: - log.error( - "'Operator' not found in info table. It may have been renamed." - ) + log.error("'Operator' not found in info table. It may have been renamed.") def start_gui() -> None: diff --git a/b_asic/scheduler_gui/operation_item.py b/b_asic/scheduler_gui/operation_item.py index eb3b331d..244723ba 100644 --- a/b_asic/scheduler_gui/operation_item.py +++ b/b_asic/scheduler_gui/operation_item.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -B-ASIC Scheduler-gui Graphics Component Item Module. +B-ASIC Scheduler-GUI Operation Item Module. -Contains the scheduler-gui OperationItem class for drawing and maintain a component -in a graph. +Contains the scheduler_gui OperationItem class for drawing and maintain an operation +in the schedule. """ from typing import TYPE_CHECKING, Dict, List, Union, cast @@ -34,23 +34,24 @@ if TYPE_CHECKING: class OperationItem(QGraphicsItemGroup): - """ + f""" Class to represent an operation in a graph. Parameters ---------- operation : :class:`~b_asic.operation.Operation` + The operation. parent : :class:`~b_asic.scheduler_gui.scheduler_item.SchedulerItem` - height : float, default: 1.0 + Parent passed to QGraphicsItemGroup + height : float, default: {OPERATION_HEIGHT} + The height of the operation. """ _scale: float = 1.0 """Static, changed from MainWindow.""" _operation: Operation _height: float - _ports: Dict[ - str, Dict[str, Union[float, QPointF]] - ] # ['port-id']['latency/pos'] + _ports: Dict[str, Dict[str, Union[float, QPointF]]] # ['port-id']['latency/pos'] _end_time: int _latency_item: QGraphicsPathItem _execution_time_item: QGraphicsPathItem @@ -72,9 +73,7 @@ class OperationItem(QGraphicsItemGroup): self._height = height operation._check_all_latencies_set() latency_offsets = cast(Dict[str, int], operation.latency_offsets) - self._ports = { - k: {"latency": float(v)} for k, v in latency_offsets.items() - } + self._ports = {k: {"latency": float(v)} for k, v in latency_offsets.items()} self._end_time = max(latency_offsets.values()) self._port_items = [] @@ -122,6 +121,14 @@ class OperationItem(QGraphicsItemGroup): @height.setter def height(self, height: float) -> None: + """ + Set height. + + Parameters + ---------- + height : float + The new height. + """ if self._height != height: self.clear() self._height = height @@ -168,9 +175,7 @@ class OperationItem(QGraphicsItemGroup): 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 = 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 @@ -178,9 +183,7 @@ class OperationItem(QGraphicsItemGroup): Qt.RoundJoin ) # Qt.MiterJoin, Qt.BevelJoin (default), Qt.RoundJoin, Qt.SvgMiterJoin - port_filling_brush = QBrush( - Qt.GlobalColor.black - ) # used by port filling + 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) @@ -214,9 +217,7 @@ class OperationItem(QGraphicsItemGroup): self._execution_time_item.setPen(execution_time_pen) # component item - self._set_background( - OPERATION_LATENCY_INACTIVE - ) # used by component filling + self._set_background(OPERATION_LATENCY_INACTIVE) # used by component filling inputs, outputs = self._operation.get_io_coordinates() diff --git a/b_asic/scheduler_gui/scheduler_event.py b/b_asic/scheduler_gui/scheduler_event.py index 36b99f5f..b1b9c75c 100644 --- a/b_asic/scheduler_gui/scheduler_event.py +++ b/b_asic/scheduler_gui/scheduler_event.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -B-ASIC Scheduler-gui Graphics Graph Event Module. +B-ASIC Scheduler-GUI Graphics Scheduler Event Module. -Contains the scheduler-gui SchedulerEvent class containing event filters and +Contains the scheduler_ui SchedulerEvent class containing event filters and handlers for SchedulerItem objects. """ @@ -61,7 +61,7 @@ class SchedulerEvent: # PyQt5 def is_valid_delta_time(self, delta_time: int) -> bool: raise NotImplementedError - def set_schedule_time(self, delta_time: int) -> None: + def change_schedule_time(self, delta_time: int) -> None: raise NotImplementedError def set_item_active(self, item: OperationItem) -> None: @@ -78,9 +78,7 @@ class SchedulerEvent: # PyQt5 ... @overload - def installSceneEventFilters( - self, filterItems: List[QGraphicsItem] - ) -> None: + def installSceneEventFilters(self, filterItems: List[QGraphicsItem]) -> None: ... def installSceneEventFilters(self, filterItems) -> None: @@ -98,9 +96,7 @@ class SchedulerEvent: # PyQt5 ... @overload - def removeSceneEventFilters( - self, filterItems: List[QGraphicsItem] - ) -> None: + def removeSceneEventFilters(self, filterItems: List[QGraphicsItem]) -> None: ... def removeSceneEventFilters(self, filterItems) -> None: @@ -166,47 +162,31 @@ class SchedulerEvent: # PyQt5 def operation_focusInEvent(self, event: QFocusEvent) -> None: ... - def operation_contextMenuEvent( - self, event: QGraphicsSceneContextMenuEvent - ) -> None: + def operation_contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None: ... - def operation_dragEnterEvent( - self, event: QGraphicsSceneDragDropEvent - ) -> None: + def operation_dragEnterEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ... - def operation_dragMoveEvent( - self, event: QGraphicsSceneDragDropEvent - ) -> None: + def operation_dragMoveEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ... - def operation_dragLeaveEvent( - self, event: QGraphicsSceneDragDropEvent - ) -> None: + def operation_dragLeaveEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ... def operation_dropEvent(self, event: QGraphicsSceneDragDropEvent) -> None: ... - def operation_hoverEnterEvent( - self, event: QGraphicsSceneHoverEvent - ) -> None: + def operation_hoverEnterEvent(self, event: QGraphicsSceneHoverEvent) -> None: ... - def operation_hoverMoveEvent( - self, event: QGraphicsSceneHoverEvent - ) -> None: + def operation_hoverMoveEvent(self, event: QGraphicsSceneHoverEvent) -> None: ... - def operation_hoverLeaveEvent( - self, event: QGraphicsSceneHoverEvent - ) -> None: + def operation_hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent) -> None: ... - def operation_mouseMoveEvent( - self, event: QGraphicsSceneMouseEvent - ) -> None: + def operation_mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: """ Set the position of the graphical element in the graphic scene, translate coordinates of the cursor within the graphic element in the @@ -215,18 +195,14 @@ class SchedulerEvent: # PyQt5 def update_pos(operation_item, dx, dy): pos_x = operation_item.x() + dx - pos_y = operation_item.y() + dy * ( - OPERATION_GAP + OPERATION_HEIGHT - ) + pos_y = operation_item.y() + dy * (OPERATION_GAP + OPERATION_HEIGHT) if self.is_component_valid_pos(operation_item, pos_x): operation_item.setX(pos_x) operation_item.setY(pos_y) self._current_pos.setX(self._current_pos.x() + dx) self._current_pos.setY(self._current_pos.y() + dy) self._redraw_lines(operation_item) - self._schedule._y_locations[ - operation_item.operation.graph_id - ] += dy + self._schedule._y_locations[operation_item.operation.graph_id] += dy item: OperationItem = self.scene().mouseGrabberItem() delta_x = (item.mapToParent(event.pos()) - self._current_pos).x() @@ -240,9 +216,7 @@ class SchedulerEvent: # PyQt5 elif delta_y_steps != 0: update_pos(item, 0, delta_y_steps) - def operation_mousePressEvent( - self, event: QGraphicsSceneMouseEvent - ) -> None: + def operation_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 @@ -255,9 +229,7 @@ class SchedulerEvent: # PyQt5 self.set_item_active(item) event.accept() - def operation_mouseReleaseEvent( - self, event: QGraphicsSceneMouseEvent - ) -> None: + def operation_mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: """Change the cursor to OpenHandCursor when releasing an object.""" item: OperationItem = self.scene().mouseGrabberItem() self.set_item_inactive(item) @@ -275,9 +247,7 @@ class SchedulerEvent: # PyQt5 self._redraw_lines(item) self._signals.component_moved.emit(item.graph_id) - def operation_mouseDoubleClickEvent( - self, event: QGraphicsSceneMouseEvent - ) -> None: + def operation_mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent) -> None: ... def operation_wheelEvent(self, event: QGraphicsSceneWheelEvent) -> None: @@ -309,9 +279,7 @@ class SchedulerEvent: # PyQt5 elif delta_x < -0.505: update_pos(item, -1) - def timeline_mousePressEvent( - self, event: QGraphicsSceneMouseEvent - ) -> None: + def timeline_mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None: """ Store the current position in item's parent coordinates. *event* will by default be accepted, and this item is then the mouse grabber. This @@ -324,12 +292,10 @@ class SchedulerEvent: # PyQt5 self._current_pos = item.mapToParent(event.pos()) event.accept() - def timeline_mouseReleaseEvent( - self, event: QGraphicsSceneMouseEvent - ) -> None: + def timeline_mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None: """Updates the schedule time.""" item: TimelineItem = self.scene().mouseGrabberItem() item.hide_label() if self._delta_time != 0: - self.set_schedule_time(self._delta_time) + self.change_schedule_time(self._delta_time) self._signals.schedule_time_changed.emit() diff --git a/b_asic/scheduler_gui/scheduler_item.py b/b_asic/scheduler_gui/scheduler_item.py index b59202c9..0e8a0443 100644 --- a/b_asic/scheduler_gui/scheduler_item.py +++ b/b_asic/scheduler_gui/scheduler_item.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -B-ASIC Scheduler-gui Graphics Graph Item Module. +B-ASIC Scheduler-GUI Scheduler Item Module. -Contains the scheduler-gui SchedulerItem class for drawing and -maintain a component in a graph. +Contains the scheduler_gui SchedulerItem class for drawing and +maintaining a schedule. """ from collections import defaultdict from math import floor @@ -31,7 +31,9 @@ from b_asic.scheduler_gui.signal_item import SignalItem class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 """ - A class to represent a graph in a QGraphicsScene. This class is a + A class to represent a schedule in a QGraphicsScene. + + This class is a subclass of QGraphicsItemGroup and contains the objects, axes from AxesItem, as well as components from OperationItem. It also inherits from SchedulerEvent, which acts as a filter for events @@ -53,9 +55,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 _event_items: List[QGraphicsItem] _signal_dict: Dict[OperationItem, Set[SignalItem]] - def __init__( - self, schedule: Schedule, parent: Optional[QGraphicsItem] = None - ): + def __init__(self, schedule: Schedule, parent: Optional[QGraphicsItem] = None): """ Construct a SchedulerItem. *parent* is passed to QGraphicsItemGroup's constructor. @@ -176,27 +176,23 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 def is_valid_delta_time(self, delta_time: int) -> bool: """ - Takes in a delta time and returns True if the schedule time can be changed by - *delta_time*. False otherwise. + Return True if the schedule time can be changed by *delta_time*. """ # TODO: implement # item = self.scene().mouseGrabberItem() if self.schedule is None: raise ValueError("No schedule installed.") return ( - self.schedule.schedule_time + delta_time - >= self.schedule.get_max_end_time() + self.schedule.schedule_time + delta_time >= self.schedule.get_max_end_time() ) - def set_schedule_time(self, delta_time: int) -> None: + def change_schedule_time(self, delta_time: int) -> None: """Change the schedule time by *delta_time* and redraw the graph.""" if self._axes is None: raise RuntimeError("No AxesItem!") if self.schedule is None: raise ValueError("No schedule installed.") - self.schedule.set_schedule_time( - self.schedule.schedule_time + delta_time - ) + self.schedule.set_schedule_time(self.schedule.schedule_time + delta_time) self._axes.set_width(self._axes.width + delta_time) # Redraw all lines self._redraw_all_lines() @@ -223,9 +219,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 op_item = self._operation_items[graph_id] op_item.setPos( self._x_axis_indent + self.schedule.start_times[graph_id], - self.schedule._get_y_position( - graph_id, OPERATION_HEIGHT, OPERATION_GAP - ), + self.schedule._get_y_position(graph_id, OPERATION_HEIGHT, OPERATION_GAP), ) def _redraw_from_start(self) -> None: @@ -261,9 +255,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 # build components for graph_id in self.schedule.start_times.keys(): operation = cast(Operation, self.schedule.sfg.find_by_id(graph_id)) - component = OperationItem( - operation, height=OPERATION_HEIGHT, parent=self - ) + component = OperationItem(operation, height=OPERATION_HEIGHT, parent=self) self._operation_items[graph_id] = component self._set_position(graph_id) self._event_items += component.event_items diff --git a/b_asic/scheduler_gui/signal_item.py b/b_asic/scheduler_gui/signal_item.py index 8a00a331..d1d1d27a 100644 --- a/b_asic/scheduler_gui/signal_item.py +++ b/b_asic/scheduler_gui/signal_item.py @@ -1,3 +1,11 @@ +""" +B-ASIC Scheduler-GUI Signal Item Module. + +Contains the scheduler_gui SignalItem class for drawing and maintaining a signal +in the schedule. +""" + + from typing import TYPE_CHECKING, Optional, cast from qtpy.QtCore import QPointF @@ -24,14 +32,14 @@ class SignalItem(QGraphicsPathItem): Parameters ---------- - src_operation : OperationItem + src_operation : `~b_asic.scheduler_gui.operation_item.OperationItem` The operation that the signal is drawn from. - dest_operation : OperationItem + dest_operation : `~b_asic.scheduler_gui.operation_item.OperationItem` The operation that the signal is drawn to. - signal : Signal + signal : `~b_asic.signal.Signal` The signal on the SFG level. parent : QGraphicsItem, optional - The parent QGraphicsItem. + The parent QGraphicsItem passed to QGraphicsPathItem. """ _path: Optional[QPainterPath] = None @@ -75,7 +83,6 @@ class SignalItem(QGraphicsPathItem): schedule = cast("SchedulerItem", self.parentItem()).schedule if dest_x - source_x <= -0.1 or schedule._laps[self._signal.graph_id]: offset = SCHEDULE_INDENT # TODO: Get from parent/axes... - laps = schedule._laps[self._signal.graph_id] path.lineTo(schedule.schedule_time + offset, source_y) path.moveTo(0 + offset, dest_y) path.lineTo(dest_x, dest_y) diff --git a/b_asic/scheduler_gui/timeline_item.py b/b_asic/scheduler_gui/timeline_item.py index 97908ac5..96509c25 100644 --- a/b_asic/scheduler_gui/timeline_item.py +++ b/b_asic/scheduler_gui/timeline_item.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -B-ASIC Scheduler-gui Graphics Timeline Item Module. +B-ASIC Scheduler-GUI Timeline Item Module. -Contains the scheduler-gui TimelineItem class for drawing and -maintain the timeline in a graph. +Contains the scheduler_gui TimelineItem class for drawing and +maintain the timeline in a schedule. """ from typing import List, Optional, overload @@ -21,9 +21,7 @@ class TimelineItem(QGraphicsLineItem): _delta_time_label: QGraphicsTextItem @overload - def __init__( - self, line: QLineF, parent: Optional[QGraphicsItem] = None - ) -> None: + def __init__(self, line: QLineF, parent: Optional[QGraphicsItem] = None) -> None: """ Constructs a TimelineItem out of 'line'. 'parent' is passed to QGraphicsLineItem's constructor. -- GitLab