Newer
Older
Contains the scheduler_gui MainWindow class for scheduling operations in an SFG.
Start main-window with ``start_gui()``.
import webbrowser
from collections import defaultdict, deque
from copy import deepcopy
from importlib.machinery import SourceFileLoader
from typing import TYPE_CHECKING, Deque, Dict, List, Optional, cast, overload
from qtpy.QtCore import (
QByteArray,
QCoreApplication,
QRectF,
QSettings,
QStandardPaths,
Qt,
Slot,
)
from qtpy.QtGui import QCloseEvent, QColor, QFont, QIcon, QIntValidator, QPalette
QColorDialog,
QDialog,
QDialogButtonBox,
import b_asic.logger as logger
from b_asic._version import __version__
from b_asic.gui_utils.about_window import AboutWindow
from b_asic.gui_utils.color_button import ColorButton
from b_asic.gui_utils.mpl_window import MPLWindow
from b_asic.scheduler_gui._preferences import (
Active_Color,
ColorDataType,
Execution_Time_Color,
Font,
Latency_Color,
Signal_Color,
Signal_Warning_Color,
)
from b_asic.scheduler_gui.axes_item import AxesItem
from b_asic.scheduler_gui.operation_item import OperationItem
from b_asic.scheduler_gui.scheduler_item import SchedulerItem
from b_asic.scheduler_gui.ui_main_window import Ui_MainWindow
log: "Logger" = logger.getLogger(__name__, "scheduler-gui.log")
if __debug__:
# Print some system version information
log.debug(f"Qt version (runtime): {QtCore.qVersion()}")
log.debug(f"Qt version (compile time): {QtCore.__version__}")
log.debug(f"QT_API: {QT_API}")
log.debug(f"PySide version: {PySide6.__version__}")
log.debug(f"PyQt version: {PYQT_VERSION_STR}")
log.debug(f"QtPy version: {qtpy.__version__}")
# The following QCoreApplication values is used for QSettings among others
QCoreApplication.setOrganizationName("Linköping University")
QCoreApplication.setApplicationName("B-ASIC Scheduling GUI")
QCoreApplication.setApplicationVersion(__version__)
class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
_schedule: Optional[Schedule]
_graph: Optional[SchedulerItem]
_debug_rectangles: QGraphicsItemGroup
_color_per_type: Dict[str, QColor] = dict()
converted_colorPerType: Dict[str, str] = dict()
super().__init__()
self._schedule = None
self._graph = None
self._scale = 75.0
self.setupUi(self)
self._read_settings()
self._init_ui()
self._show_incorrect_execution_time = True
self._execution_time_for_variables = None
self._execution_time_plot_dialogs = defaultdict(lambda: None)
self._ports_accesses_for_storage = None
self._color_changed_perType = False
self.changed_operation_colors: Dict[str, QColor] = dict()
# Recent files
self._max_recent_files = 4
self._recent_files_actions: List[QAction] = []
self._recent_file_paths: Deque[str] = deque(maxlen=self._max_recent_files)
self._create_recent_file_actions_and_menus()
self._init_graphics()
def _init_ui(self) -> None:
"""Initialize the ui"""
# Connect signals to slots
self.menu_load_from_file.triggered.connect(self._load_schedule_from_pyfile)
self.menu_load_from_file.setIcon(get_icon('import'))
self.menu_open.setIcon(get_icon('open'))
self.menu_open.triggered.connect(self.open_schedule)
self.menu_close_schedule.triggered.connect(self.close_schedule)
self.menu_close_schedule.setIcon(get_icon('close'))
self.actionPreferences.triggered.connect(self.Preferences_Dialog_clicked)
self.menu_node_info.triggered.connect(self.show_info_table)
self.menu_exit_dialog.triggered.connect(self.hide_exit_dialog)
self.actionReorder.triggered.connect(self._action_reorder)
self.actionReorder.setIcon(get_icon('reorder'))
self.actionStatus_bar.triggered.connect(self._toggle_statusbar)
self.action_incorrect_execution_time.setIcon(get_icon('warning'))
self.action_incorrect_execution_time.triggered.connect(
self._toggle_execution_time_warning
)
self.action_show_port_numbers.setIcon(get_icon('port-numbers'))
self.action_show_port_numbers.triggered.connect(self._toggle_port_number)
self.actionPlot_schedule.setIcon(get_icon('plot-schedule'))
self.actionPlot_schedule.triggered.connect(self._plot_schedule)
self.action_view_variables.triggered.connect(
self._show_execution_times_for_variables
)
self.action_view_port_accesses.triggered.connect(
self._show_ports_accesses_for_storage
)
self.actionZoom_to_fit.setIcon(get_icon('zoom-to-fit'))
self.actionZoom_to_fit.triggered.connect(self._zoom_to_fit)
self.actionToggle_full_screen.setIcon(get_icon('full-screen'))
self.actionToggle_full_screen.triggered.connect(self._toggle_fullscreen)
self.actionUndo.setIcon(get_icon('undo'))
self.actionRedo.setIcon(get_icon('redo'))
self.splitter.splitterMoved.connect(self._splitter_moved)
self.actionDocumentation.triggered.connect(self._open_documentation)
self.actionAbout.triggered.connect(self._open_about_window)
self.actionDecrease_time_resolution.triggered.connect(
self._decrease_time_resolution
)
self.actionDecrease_time_resolution.setIcon(get_icon('decrease-timeresolution'))
self.actionIncrease_time_resolution.triggered.connect(
self._increase_time_resolution
)
self.actionIncrease_time_resolution.setIcon(get_icon('increase-timeresolution'))
# Setup event member functions
self.closeEvent = self._close_event
# Setup info table
self.info_table.setSpan(0, 0, 1, 2) # Span 'Schedule' over 2 columns
self.info_table.setSpan(1, 0, 1, 2) # Span 'Operator' over 2 columns
# Init central-widget splitter
self._splitter_min = self.splitter.minimumSizeHint().height()
self.splitter.setStretchFactor(0, 1)
self.splitter.setStretchFactor(1, 0)
self.splitter.setCollapsible(0, False)
self.splitter.setCollapsible(1, True)
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)
OperationItem._scale = self._scale
AxesItem._scale = self._scale
self._scene.sceneRectChanged.connect(self.shrink_scene_to_min_size)
@property
#########
# Slots #
#########
def _plot_schedule(self) -> None:
"""Callback for plotting schedule using Matplotlib."""
@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 _action_reorder(self) -> None:
"""Callback to reorder all operations vertically based on start time."""
if self.schedule is None:
return
if self._graph is not None:
self._graph._redraw_from_start()
self.update_statusbar("Operations reordered based on start time")
@Slot()
def _increase_time_resolution(self) -> None:
"""Callback for increasing time resolution."""
# Create dialog asking for int
factor, ok = QInputDialog.getInt(
self, "Increase time resolution", "Factor", 1, 1
)
# Check return value
if ok:
if factor > 1:
self.schedule.increase_time_resolution(factor)
self.open(self.schedule)
print(f"schedule.increase_time_resolution({factor})")
self.update_statusbar(f"Time resolution increased by a factor {factor}")
else: # Cancelled
@Slot()
def _decrease_time_resolution(self) -> None:
"""Callback for decreasing time resolution."""
# Get possible factors
vals = [str(v) for v in self.schedule.get_possible_time_resolution_decrements()]
# Create dialog
factor, ok = QInputDialog.getItem(
self, "Decrease time resolution", "Factor", vals, editable=False
)
# Check return value
if ok:
if int(factor) > 1:
self.schedule.decrease_time_resolution(int(factor))
self.open(self.schedule)
print(f"schedule.decrease_time_resolution({factor})")
self.update_statusbar(f"Time resolution decreased by a factor {factor}")
else: # Cancelled
def wheelEvent(self, event) -> None:
"""Zoom in or out using mouse wheel if control is pressed."""
if event.modifiers() == Qt.KeyboardModifier.ControlModifier:
old_zoom = self._zoom
self._zoom += event.angleDelta().y() / 2500
self.view.scale(self._zoom, self._zoom)
self._zoom = old_zoom
Loading
Loading full blame...