Skip to content
Snippets Groups Projects
Commit fa15c7a4 authored by Oscar Gustafsson's avatar Oscar Gustafsson :bicyclist:
Browse files

Fix documentation for scheduler GUI

parent ddfe0a4a
No related branches found
No related tags found
1 merge request!211Fix documentation for scheduler GUI
Pipeline #90033 passed
#!/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()
......
......@@ -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
......
#!/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.
......
#!/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:
......
#!/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()
......
#!/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()
#!/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
......
"""
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)
......
#!/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.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment