From 4eeb79202045b089534a3f4d1ead694e8c3675a6 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson <oscar.gustafsson@gmail.com> Date: Thu, 23 Feb 2023 10:26:49 +0100 Subject: [PATCH] Fix moving operations in y-direction --- b_asic/schedule.py | 74 ++++++++++++++++++++++++- b_asic/scheduler_gui/main_window.py | 4 ++ b_asic/scheduler_gui/scheduler_event.py | 27 +++++---- b_asic/scheduler_gui/scheduler_item.py | 15 +++-- 4 files changed, 103 insertions(+), 17 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 2b725089..6eaa7111 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -31,7 +31,7 @@ from b_asic._preferences import ( from b_asic.graph_component import GraphID from b_asic.operation import Operation from b_asic.port import InputPort, OutputPort -from b_asic.process import MemoryVariable, Process +from b_asic.process import MemoryVariable from b_asic.resources import ProcessCollection from b_asic.signal_flow_graph import SFG from b_asic.special_operations import Delay, Input, Output @@ -244,6 +244,7 @@ class Schedule: return self.backward_slack(graph_id), self.forward_slack(graph_id) def print_slacks(self) -> None: + """Print the slack times for all operations in the schedule.""" raise NotImplementedError def set_schedule_time(self, time: int) -> "Schedule": @@ -281,10 +282,12 @@ class Schedule: @property def schedule_time(self) -> int: + """The schedule time of the current schedule.""" return self._schedule_time @property def cyclic(self) -> bool: + """If the current schedule is cyclic.""" return self._cyclic def increase_time_resolution(self, factor: int) -> "Schedule": @@ -360,6 +363,75 @@ class Schedule: self._schedule_time = self._schedule_time // factor return self + def move_y_location( + self, graph_id: GraphID, new_y: int, insert: bool = False + ) -> None: + """ + Move operation in y-direction and remove any empty rows. + + Parameters + ---------- + graph_id : GraphID + The GraphID of the operation to move. + new_y : int + The new y-position of the operation. + insert : bool, optional + If True, all operations on that y-position will be moved one position. + The default is False. + + """ + if insert: + for gid, y_location in self._y_locations.items(): + if y_location >= new_y: + self._y_locations[gid] += 1 + self._y_locations[graph_id] = new_y + used_locations = {*self._y_locations.values()} + possible_locations = set(range(max(used_locations) + 1)) + if not possible_locations - used_locations: + return + remapping = {} + offset = 0 + for loc in possible_locations: + if loc in used_locations: + remapping[loc] = loc - offset + else: + offset += 1 + + for gid, y_location in self._y_locations.items(): + self._y_locations[gid] = remapping[self._y_locations[gid]] + + def get_y_location(self, graph_id: GraphID) -> int: + """ + Get the y-position of the Operation with GraphID *graph_id*. + + Parameters + ---------- + graph_id : GraphID + The GraphID of the operation. + + Returns + ------- + int + The y-position of the operation. + + """ + return self._y_locations[graph_id] + + def set_y_location(self, graph_id: GraphID, y_location: int) -> None: + """ + Set the y-position of the Operation with GraphID *graph_id* to *y_location*. + + + Parameters + ---------- + graph_id : GraphID + The GraphID of the operation to move. + y_location : int + The new y-position of the operation. + + """ + self._y_locations[graph_id] = y_location + def move_operation(self, graph_id: GraphID, time: int) -> "Schedule": """ Move an operation in the schedule. diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index 8d56996e..28c6e550 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -482,9 +482,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): self._graph._signals.schedule_time_changed.connect( self.info_table_update_schedule ) + self._graph._signals.redraw_all.connect(self._redraw_all) self.info_table_fill_schedule(self._schedule) self.update_statusbar(self.tr("Schedule loaded successfully")) + def _redraw_all(self) -> None: + self._graph._redraw_all() + def update_statusbar(self, msg: str) -> None: """ Write *msg* to the statusbar with temporarily policy. diff --git a/b_asic/scheduler_gui/scheduler_event.py b/b_asic/scheduler_gui/scheduler_event.py index 37add599..026d90fa 100644 --- a/b_asic/scheduler_gui/scheduler_event.py +++ b/b_asic/scheduler_gui/scheduler_event.py @@ -44,12 +44,14 @@ class SchedulerEvent: # PyQt5 component_selected = Signal(str) schedule_time_changed = Signal() component_moved = Signal(str) + redraw_all = Signal() _axes: Optional[AxesItem] _current_pos: QPointF _delta_time: int _signals: Signals # PyQt5 _schedule: Schedule + _old_op_position: int = -1 def __init__(self, parent: Optional[QGraphicsItem] = None): # PyQt5 super().__init__(parent=parent) @@ -202,7 +204,10 @@ class SchedulerEvent: # PyQt5 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 + gid = operation_item.operation.graph_id + self._schedule.set_y_location( + gid, dy + self._schedule.get_y_location(gid) + ) item: OperationItem = self.scene().mouseGrabberItem() delta_x = (item.mapToParent(event.pos()) - self._current_pos).x() @@ -224,7 +229,7 @@ class SchedulerEvent: # PyQt5 allows the item to receive future move, release and double-click events. """ item: OperationItem = self.scene().mouseGrabberItem() - self._old_op_position = self._schedule._y_locations[item.operation.graph_id] + self._old_op_position = self._schedule.get_y_location(item.operation.graph_id) self._signals.component_selected.emit(item.graph_id) self._current_pos = item.mapToParent(event.pos()) self.set_item_active(item) @@ -243,18 +248,20 @@ class SchedulerEvent: # PyQt5 if pos_x > self._schedule.schedule_time: pos_x = pos_x % self._schedule.schedule_time redraw = True - if self._schedule._y_locations[item.operation.graph_id] % 1: - # TODO: move other operations - self._schedule._y_locations[item.operation.graph_id] = math.ceil( - self._schedule._y_locations[item.operation.graph_id] + pos_y = self._schedule.get_y_location(item.operation.graph_id) + # Check move in y-direction + if pos_y != self._old_op_position: + self._schedule.move_y_location( + item.operation.graph_id, + math.ceil(pos_y), + (pos_y % 1) != 0, ) - pos_y = item.y() + (OPERATION_GAP + OPERATION_HEIGHT) / 2 - item.setY(pos_y) - redraw = True + self._signals.redraw_all.emit() + # Operation has been moved in x-direction if redraw: item.setX(pos_x) self._redraw_lines(item) - self._signals.component_moved.emit(item.graph_id) + self._signals.component_moved.emit(item.graph_id) def operation_mouseDoubleClickEvent(self, event: QGraphicsSceneMouseEvent) -> None: ... diff --git a/b_asic/scheduler_gui/scheduler_item.py b/b_asic/scheduler_gui/scheduler_item.py index 0e8a0443..eceb35d2 100644 --- a/b_asic/scheduler_gui/scheduler_item.py +++ b/b_asic/scheduler_gui/scheduler_item.py @@ -224,15 +224,18 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 def _redraw_from_start(self) -> None: self.schedule._reset_y_locations() - for graph_id in { - k: v - for k, v in sorted( - self.schedule.start_times.items(), key=lambda item: item[1] - ) - }: + for graph_id in dict( + sorted(self.schedule.start_times.items(), key=lambda item: item[1]) + ): self._set_position(graph_id) self._redraw_all_lines() + def _redraw_all(self) -> None: + for graph_id in self._operation_items: + self._set_position(graph_id) + self._redraw_all_lines() + self._update_axes() + def _update_axes(self, build=False) -> None: # build axes schedule_time = self.schedule.schedule_time -- GitLab