From b2625bbe2d8aa9120f72f9b99c1a0a1d87c7972e Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson <oscar.gustafsson@gmail.com> Date: Thu, 2 Feb 2023 09:19:43 +0100 Subject: [PATCH] Enable reordering of schedule based on start time in GUI --- b_asic/schedule.py | 118 +++++++++++++----------- b_asic/scheduler_gui/axes_item.py | 40 ++++---- b_asic/scheduler_gui/main_window.py | 28 ++++-- b_asic/scheduler_gui/main_window.ui | 46 ++++++++- b_asic/scheduler_gui/operation_item.py | 2 +- b_asic/scheduler_gui/scheduler_event.py | 2 +- b_asic/scheduler_gui/scheduler_item.py | 88 +++++++++++------- b_asic/scheduler_gui/ui_main_window.py | 33 ++++++- 8 files changed, 239 insertions(+), 118 deletions(-) diff --git a/b_asic/schedule.py b/b_asic/schedule.py index 7b8ad21d..8c7e4e46 100644 --- a/b_asic/schedule.py +++ b/b_asic/schedule.py @@ -78,19 +78,21 @@ class Schedule: else: self._schedule_time = schedule_time - def start_time_of_operation(self, op_id: GraphID) -> int: + def start_time_of_operation(self, graph_id: GraphID) -> int: """ - Get the start time of the operation with the specified by the op_id. + Get the start time of the operation with the specified by *graph_id*. """ - if op_id not in self._start_times: - raise ValueError(f"No operation with op_id {op_id} in schedule") - return self._start_times[op_id] + if graph_id not in self._start_times: + raise ValueError( + f"No operation with graph_id {graph_id} in schedule" + ) + return self._start_times[graph_id] def get_max_end_time(self) -> int: """Returns the current maximum end time among all operations.""" max_end_time = 0 - for op_id, op_start_time in self._start_times.items(): - op = cast(Operation, self._sfg.find_by_id(op_id)) + for graph_id, op_start_time in self._start_times.items(): + op = cast(Operation, self._sfg.find_by_id(graph_id)) for outport in op.outputs: max_end_time = max( max_end_time, @@ -98,11 +100,13 @@ class Schedule: ) return max_end_time - def forward_slack(self, op_id: GraphID) -> int: - if op_id not in self._start_times: - raise ValueError(f"No operation with op_id {op_id} in schedule") + def forward_slack(self, graph_id: GraphID) -> int: + if graph_id not in self._start_times: + raise ValueError( + f"No operation with graph_id {graph_id} in schedule" + ) slack = sys.maxsize - output_slacks = self._forward_slacks(op_id) + output_slacks = self._forward_slacks(graph_id) # Make more pythonic for signal_slacks in output_slacks.values(): for signal_slack in signal_slacks.values(): @@ -110,11 +114,11 @@ class Schedule: return slack def _forward_slacks( - self, op_id: GraphID + self, graph_id: GraphID ) -> Dict["OutputPort", Dict["Signal", int]]: ret = {} - start_time = self._start_times[op_id] - op = cast(Operation, self._sfg.find_by_id(op_id)) + start_time = self._start_times[graph_id] + op = cast(Operation, self._sfg.find_by_id(graph_id)) for output_port in op.outputs: output_slacks = {} available_time = start_time + cast(int, output_port.latency_offset) @@ -130,11 +134,13 @@ class Schedule: ret[output_port] = output_slacks return ret - def backward_slack(self, op_id: GraphID) -> int: - if op_id not in self._start_times: - raise ValueError(f"No operation with op_id {op_id} in schedule") + def backward_slack(self, graph_id: GraphID) -> int: + if graph_id not in self._start_times: + raise ValueError( + f"No operation with graph_id {graph_id} in schedule" + ) slack = sys.maxsize - input_slacks = self._backward_slacks(op_id) + input_slacks = self._backward_slacks(graph_id) # Make more pythonic for signal_slacks in input_slacks.values(): for signal_slack in signal_slacks.values(): @@ -142,11 +148,11 @@ class Schedule: return slack def _backward_slacks( - self, op_id: GraphID + self, graph_id: GraphID ) -> Dict[InputPort, Dict[Signal, int]]: ret = {} - start_time = self._start_times[op_id] - op = cast(Operation, self._sfg.find_by_id(op_id)) + start_time = self._start_times[graph_id] + op = cast(Operation, self._sfg.find_by_id(graph_id)) for input_port in op.inputs: input_slacks = {} usage_time = start_time + cast(int, input_port.latency_offset) @@ -162,10 +168,12 @@ class Schedule: ret[input_port] = input_slacks return ret - def slacks(self, op_id: GraphID) -> Tuple[int, int]: - if op_id not in self._start_times: - raise ValueError(f"No operation with op_id {op_id} in schedule") - return self.backward_slack(op_id), self.forward_slack(op_id) + def slacks(self, graph_id: GraphID) -> Tuple[int, int]: + if graph_id not in self._start_times: + raise ValueError( + f"No operation with graph_id {graph_id} in schedule" + ) + return self.backward_slack(graph_id), self.forward_slack(graph_id) def print_slacks(self) -> None: raise NotImplementedError @@ -212,9 +220,9 @@ class Schedule: self._start_times = { k: factor * v for k, v in self._start_times.items() } - for op_id in self._start_times: + for graph_id in self._start_times: cast( - Operation, self._sfg.find_by_id(op_id) + Operation, self._sfg.find_by_id(graph_id) )._increase_time_resolution(factor) self._schedule_time *= factor return self @@ -227,8 +235,8 @@ class Schedule: # Local values ret = [self._schedule_time, *self._start_times.values()] # Loop over operations - for op_id in self._start_times: - op = cast(Operation, self._sfg.find_by_id(op_id)) + for graph_id in self._start_times: + op = cast(Operation, self._sfg.find_by_id(graph_id)) ret += [cast(int, op.execution_time), *op.latency_offsets.values()] # Remove not set values (None) ret = [v for v in ret if v is not None] @@ -265,26 +273,28 @@ class Schedule: self._start_times = { k: v // factor for k, v in self._start_times.items() } - for op_id in self._start_times: + for graph_id in self._start_times: cast( - Operation, self._sfg.find_by_id(op_id) + Operation, self._sfg.find_by_id(graph_id) )._decrease_time_resolution(factor) self._schedule_time = self._schedule_time // factor return self - def move_operation(self, op_id: GraphID, time: int) -> "Schedule": - if op_id not in self._start_times: - raise ValueError(f"No operation with op_id {op_id} in schedule") + def move_operation(self, graph_id: GraphID, time: int) -> "Schedule": + if graph_id not in self._start_times: + raise ValueError( + f"No operation with graph_id {graph_id} in schedule" + ) - (backward_slack, forward_slack) = self.slacks(op_id) + (backward_slack, forward_slack) = self.slacks(graph_id) if not -backward_slack <= time <= forward_slack: raise ValueError - tmp_start = self._start_times[op_id] + time + tmp_start = self._start_times[graph_id] + time new_start = tmp_start % self._schedule_time # Update input laps - input_slacks = self._backward_slacks(op_id) + input_slacks = self._backward_slacks(graph_id) for in_port, signal_slacks in input_slacks.items(): tmp_usage = tmp_start + cast(int, in_port.latency_offset) new_usage = tmp_usage % self._schedule_time @@ -311,7 +321,7 @@ class Schedule: self._laps[signal.graph_id] = laps # Update output laps - output_slacks = self._forward_slacks(op_id) + output_slacks = self._forward_slacks(graph_id) for out_port, signal_slacks in output_slacks.items(): tmp_available = tmp_start + cast(int, out_port.latency_offset) new_available = tmp_available % self._schedule_time @@ -327,7 +337,7 @@ class Schedule: self._laps[signal.graph_id] = laps # Set new start time - self._start_times[op_id] = new_start + self._start_times[graph_id] = new_start return self def _remove_delays(self) -> None: @@ -434,8 +444,8 @@ class Schedule: def _get_memory_variables_list(self) -> List['Process']: ret: List['Process'] = [] - for op_id, start_time in self._start_times.items(): - slacks = self._forward_slacks(op_id) + for graph_id, start_time in self._start_times.items(): + slacks = self._forward_slacks(graph_id) for outport, signals in slacks.items(): reads = { cast(InputPort, signal.destination): slack @@ -450,8 +460,8 @@ class Schedule: ) return ret - def _get_y_position(self, op_id): - y_location = self._y_locations[op_id] + def _get_y_position(self, graph_id): + y_location = self._y_locations[graph_id] if y_location == None: # Assign the lowest row number not yet in use used = set( @@ -459,7 +469,7 @@ class Schedule: ) possible = set(range(len(self._start_times))) - used y_location = min(possible) - self._y_locations[op_id] = y_location + self._y_locations[graph_id] = y_location return OPERATION_GAP + y_location * (1 + OPERATION_GAP) def _plot_schedule(self, ax): @@ -559,9 +569,9 @@ class Schedule: yticklabels = [] ax.set_axisbelow(True) ax.grid() - for op_id, op_start_time in self._start_times.items(): - ypos = -self._get_y_position(op_id) - op = self._sfg.find_by_id(op_id) + for graph_id, op_start_time in self._start_times.items(): + ypos = -self._get_y_position(graph_id) + op = self._sfg.find_by_id(graph_id) # Rewrite to make better use of NumPy latency_coords, execution_time_coords = op.get_plot_coordinates() _x, _y = zip(*latency_coords) @@ -581,12 +591,12 @@ class Schedule: linewidth=3, ) ytickpositions.append(ypos + 0.5) - yticklabels.append(self._sfg.find_by_id(op_id).name) + yticklabels.append(self._sfg.find_by_id(graph_id).name) - for op_id, op_start_time in self._start_times.items(): - op = self._sfg.find_by_id(op_id) + for graph_id, op_start_time in self._start_times.items(): + op = self._sfg.find_by_id(graph_id) _, out_coords = op.get_io_coordinates() - source_ypos = -self._get_y_position(op_id) + source_ypos = -self._get_y_position(graph_id) for output_port in op.outputs: for output_signal in output_port.signals: @@ -604,7 +614,7 @@ class Schedule: dest_in_coords[output_signal.destination.index], [op_start_time, source_ypos], [dest_start_time, dest_ypos], - name=op_id, + name=graph_id, laps=self._laps[output_signal.graph_id], ) @@ -612,8 +622,8 @@ class Schedule: ax.set_yticklabels(yticklabels) # Get operation with maximum position - max_pos_op_id = max(self._y_locations, key=self._y_locations.get) - yposmin = -self._get_y_position(max_pos_op_id) - OPERATION_GAP + max_pos_graph_id = max(self._y_locations, key=self._y_locations.get) + yposmin = -self._get_y_position(max_pos_graph_id) - OPERATION_GAP ax.axis([-1, self._schedule_time + 1, yposmin, 1]) ax.xaxis.set_major_locator(MaxNLocator(integer=True)) ax.add_line( diff --git a/b_asic/scheduler_gui/axes_item.py b/b_asic/scheduler_gui/axes_item.py index adea4568..bd695698 100644 --- a/b_asic/scheduler_gui/axes_item.py +++ b/b_asic/scheduler_gui/axes_item.py @@ -129,10 +129,10 @@ class AxesItem(QGraphicsItemGroup): """ return self._height - # @height.setter - # def height(self, height: int) -> None: - # if self._height != height: - # self.update_axes(height = height) + @height.setter + def height(self, height: int) -> None: + if self._height != height: + self.update_axes(height=height) # @property # def width_indent(self) -> float: @@ -154,12 +154,13 @@ class AxesItem(QGraphicsItemGroup): self._event_items.append(item) def set_height(self, height: int) -> "AxesItem": - # TODO: implement, docstring + # TODO: docstring if height < 0: raise ValueError( f"'height' greater or equal to 0 expected, got: {height}." ) - raise NotImplementedError + self._height = height + self._update_yaxis() def set_width(self, width: int) -> "AxesItem": # TODO: docstring @@ -275,6 +276,20 @@ class AxesItem(QGraphicsItemGroup): self._x_scale_labels[index + 1].setText(str(index + 1)) self._x_scale[index + 1].setX(self._x_scale[index + 1].x() + 1) + def _update_yaxis(self) -> None: + self._y_axis.setLine( + 0, + 0, + 0, + -( + self._height_indent + + self._height + + self._height_padding + + 0.05 + ), + ) + self._y_axis.setPen(self._base_pen) + def _make_base(self) -> None: # x axis self._x_axis.setLine(0, 0, self._width_indent + self._width_padding, 0) @@ -322,16 +337,5 @@ class AxesItem(QGraphicsItemGroup): ) # move timeline # y-axis - self._y_axis.setLine( - 0, - 0, - 0, - -( - self._height_indent - + self._height - + self._height_padding - + 0.05 - ), - ) - self._y_axis.setPen(self._base_pen) + self._update_yaxis() self.addToGroup(self._y_axis) diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index 936d4ebc..f94f80c3 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -10,6 +10,7 @@ Start main-window with start_gui(). import inspect import os import sys +import webbrowser from copy import deepcopy from importlib.machinery import SourceFileLoader from typing import Optional, Union, cast @@ -123,8 +124,10 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.menu_quit.triggered.connect(self.close) 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._actionReorder) self.actionT.triggered.connect(self._actionTbtn) self.splitter.splitterMoved.connect(self._splitter_moved) + self.actionDocumentation.triggered.connect(self._open_documentation) # Setup event member functions self.closeEvent = self._close_event @@ -170,6 +173,19 @@ class MainWindow(QMainWindow, Ui_MainWindow): print(f"filtersChildEvents(): {self._graph.filtersChildEvents()}") # self._printButtonPressed('callback_pushButton()') + @Slot() + def _open_documentation(self) -> None: + webbrowser.open_new_tab("https://da.gitlab-pages.liu.se/B-ASIC/") + + @Slot() + def _actionReorder(self) -> None: + # TODO: remove + if self.schedule is None: + return + if self._graph is not None: + self._graph._redraw_from_start() + # self._printButtonPressed('callback_pushButton()') + @Slot() def _load_schedule_from_pyfile(self) -> None: """SLOT() for SIGNAL(menu_load_from_file.triggered) @@ -345,15 +361,15 @@ class MainWindow(QMainWindow, Ui_MainWindow): self._splitter_pos = width @Slot(str) - def info_table_update_component(self, op_id: GraphID) -> None: + def info_table_update_component(self, graph_id: GraphID) -> None: """ SLOT(str) for SIGNAL(_graph._signals.component_selected) Takes 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'. + associated with *graph_id*. """ self.info_table_clear_component() - self._info_table_fill_component(op_id) + self._info_table_fill_component(graph_id) @Slot() def info_table_update_schedule(self) -> None: @@ -508,15 +524,15 @@ class MainWindow(QMainWindow, Ui_MainWindow): ) self.info_table.setItem(2, 1, QTableWidgetItem(str(schedule.cyclic))) - def _info_table_fill_component(self, op_id: GraphID) -> None: + def _info_table_fill_component(self, graph_id: GraphID) -> None: """ Take an operator-id and fill in the 'Operator' part of the info - table with values from the operator associated with *op_id*. + table with values from the operator associated with *graph_id*. """ if self.schedule is None: return op: GraphComponent = cast( - GraphComponent, self.schedule.sfg.find_by_id(op_id) + GraphComponent, self.schedule.sfg.find_by_id(graph_id) ) si = self.info_table.rowCount() # si = start index diff --git a/b_asic/scheduler_gui/main_window.ui b/b_asic/scheduler_gui/main_window.ui index 38ac4579..539d3281 100644 --- a/b_asic/scheduler_gui/main_window.ui +++ b/b_asic/scheduler_gui/main_window.ui @@ -18,7 +18,7 @@ </sizepolicy> </property> <property name="windowIcon"> - <iconset resource="icons/basic.qrc"> + <iconset> <normaloff>:/icons/basic/small_logo.png</normaloff>:/icons/basic/small_logo.png</iconset> </property> <widget class="QWidget" name="centralwidget"> @@ -108,6 +108,12 @@ <property name="text"> <string>Property</string> </property> + <property name="font"> + <font> + <weight>50</weight> + <bold>false</bold> + </font> + </property> <property name="textAlignment"> <set>AlignLeading|AlignVCenter</set> </property> @@ -126,7 +132,9 @@ </property> <property name="font"> <font> - <bold>true</bold> + <weight>50</weight> + <bold>false</bold> + <kerning>true</kerning> </font> </property> <property name="background"> @@ -157,7 +165,8 @@ </property> <property name="font"> <font> - <bold>true</bold> + <weight>50</weight> + <bold>false</bold> </font> </property> <property name="background"> @@ -224,10 +233,19 @@ </property> <addaction name="menu_exit_dialog"/> </widget> + <widget class="QMenu" name="menuHelp"> + <property name="title"> + <string>&Help</string> + </property> + <addaction name="actionDocumentation"/> + <addaction name="separator"/> + <addaction name="actionAbout"/> + </widget> <addaction name="menuFile"/> <addaction name="menu_Edit"/> <addaction name="menuView"/> <addaction name="menuWindow"/> + <addaction name="menuHelp"/> </widget> <widget class="QStatusBar" name="statusbar"/> <widget class="QToolBar" name="toolBar"> @@ -246,6 +264,7 @@ <addaction name="separator"/> <addaction name="menu_node_info"/> <addaction name="actionT"/> + <addaction name="actionReorder"/> </widget> <action name="menu_load_from_file"> <property name="icon"> @@ -291,7 +310,7 @@ <bool>true</bool> </property> <property name="icon"> - <iconset resource="icons/misc.qrc"> + <iconset> <normaloff>:/icons/misc/right_panel.svg</normaloff> <normalon>:/icons/misc/right_filled_panel.svg</normalon>:/icons/misc/right_panel.svg</iconset> </property> @@ -364,6 +383,25 @@ <string>&Close Schedule</string> </property> </action> + <action name="actionAbout"> + <property name="text"> + <string>About</string> + </property> + </action> + <action name="actionDocumentation"> + <property name="text"> + <string>Documentation</string> + </property> + </action> + <action name="actionReorder"> + <property name="text"> + <string>Reorder</string> + </property> + <property name="toolTip"> + <string>Reorder schedule based on start time</string> + </property> + </action> </widget> + <resources/> <connections/> </ui> diff --git a/b_asic/scheduler_gui/operation_item.py b/b_asic/scheduler_gui/operation_item.py index b67a3840..5321b722 100644 --- a/b_asic/scheduler_gui/operation_item.py +++ b/b_asic/scheduler_gui/operation_item.py @@ -90,7 +90,7 @@ class OperationItem(QGraphicsItemGroup): del item @property - def op_id(self) -> GraphID: + def graph_id(self) -> GraphID: """Get the op-id.""" return self._operation.graph_id diff --git a/b_asic/scheduler_gui/scheduler_event.py b/b_asic/scheduler_gui/scheduler_event.py index 5dc2a4d4..645eafba 100644 --- a/b_asic/scheduler_gui/scheduler_event.py +++ b/b_asic/scheduler_gui/scheduler_event.py @@ -215,7 +215,7 @@ class SchedulerEvent: # PyQt5 allows the item to receive future move, release and double-click events. """ item: OperationItem = self.scene().mouseGrabberItem() - self._signals.component_selected.emit(item.op_id) + self._signals.component_selected.emit(item.graph_id) self._current_pos = item.mapToParent(event.pos()) self.set_item_active(item) event.accept() diff --git a/b_asic/scheduler_gui/scheduler_item.py b/b_asic/scheduler_gui/scheduler_item.py index 810c568b..804b2b9d 100644 --- a/b_asic/scheduler_gui/scheduler_item.py +++ b/b_asic/scheduler_gui/scheduler_item.py @@ -34,7 +34,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 """ _axes: Optional[AxesItem] - _components: List[OperationItem] + _operation_items: Dict[str, OperationItem] _x_axis_indent: float _event_items: List[QGraphicsItem] _signal_dict: Dict[OperationItem, Set[SignalItem]] @@ -53,7 +53,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 # super().__init__(parent=self) self._schedule = schedule self._axes = None - self._components = [] + self._operation_items = {} self._x_axis_indent = 0.2 self._event_items = [] self._signal_dict = defaultdict(set) @@ -74,8 +74,8 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 assert self.schedule is not None, "No schedule installed." end_time = item.end_time new_start_time = floor(pos) - floor(self._x_axis_indent) - slacks = self.schedule.slacks(item.op_id) - op_start_time = self.schedule.start_time_of_operation(item.op_id) + slacks = self.schedule.slacks(item.graph_id) + op_start_time = self.schedule.start_time_of_operation(item.graph_id) if not -slacks[0] <= new_start_time - op_start_time <= slacks[1]: # Cannot move due to dependencies return False @@ -94,6 +94,13 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 return True + def _redraw_all_lines(self): + s = set() + for signals in self._signal_dict.values(): + s.update(signals) + for signal in s: + signal.update_path() + def _redraw_lines(self, item: OperationItem): """Update lines connected to *item*.""" for signal in self._signal_dict[item]: @@ -112,11 +119,11 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 def set_new_starttime(self, item: OperationItem) -> None: """Set new starttime for *item*.""" pos = item.x() - op_start_time = self.schedule.start_time_of_operation(item.op_id) + op_start_time = self.schedule.start_time_of_operation(item.graph_id) new_start_time = floor(pos) - floor(self._x_axis_indent) move_time = new_start_time - op_start_time if move_time: - self.schedule.move_operation(item.op_id, move_time) + self.schedule.move_operation(item.graph_id, move_time) def is_valid_delta_time(self, delta_time: int) -> bool: """Takes in a delta time and returns true if the new schedule time is @@ -139,9 +146,7 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 ) self._axes.set_width(self._axes.width + delta_time) # Redraw all lines - for signals in self._signal_dict.values(): - for signal in signals: - signal.update_path() + self._redraw_all_lines() @property def schedule(self) -> Schedule: @@ -154,53 +159,72 @@ class SchedulerItem(SchedulerEvent, QGraphicsItemGroup): # PySide2 / PyQt5 @property def components(self) -> List[OperationItem]: - return self._components + return list(component for component in self._operation_items.values()) @property def event_items(self) -> List[QGraphicsItem]: """Return a list of objects that receives events.""" return self._event_items - def _make_graph(self) -> None: - """Makes a new graph out of the stored attributes.""" - # build components - _components_dict = {} - # print('Start times:') - for op_id, op_start_time in self.schedule.start_times.items(): - operation = cast(Operation, self.schedule.sfg.find_by_id(op_id)) - component = OperationItem(operation, parent=self) - component.setPos( - self._x_axis_indent + op_start_time, - self.schedule._get_y_position(op_id), + def _set_position(self, graph_id) -> None: + 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), + ) + + 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] ) - self._components.append(component) - _components_dict[operation] = component - self._event_items += component.event_items + }: + self._set_position(graph_id) + self._redraw_all_lines() + def _update_axes(self, build=False): # build axes schedule_time = self.schedule.schedule_time - max_pos_op_id = max( + max_pos_graph_id = max( self.schedule._y_locations, key=self.schedule._y_locations.get ) - yposmin = self.schedule._get_y_position(max_pos_op_id) + yposmin = self.schedule._get_y_position(max_pos_graph_id) - self._axes = AxesItem(schedule_time, yposmin + 0.5) + if self._axes is None or build: + self._axes = AxesItem(schedule_time, yposmin + 0.5) + self._event_items += self._axes.event_items + else: + self._axes.set_height(yposmin + 0.5) self._axes.setPos(0, yposmin + 1 + OPERATION_GAP) - self._event_items += self._axes.event_items + + def _make_graph(self) -> None: + """Makes a new graph out of the stored attributes.""" + # 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, parent=self) + self._operation_items[graph_id] = component + self._set_position(graph_id) + self._event_items += component.event_items + # self._axes.width = schedule_time # add axes and components + self._update_axes(build=True) self.addToGroup(self._axes) - for component in self._components: + for component in self.components: self.addToGroup(component) - # self.addToGroup(self._components) # add signals - for component in self._components: + for component in self.components: for output_port in component.operation.outputs: for signal in output_port.signals: destination = cast(InputPort, signal.destination) - dest_component = _components_dict[destination.operation] + dest_component = self._operation_items[ + destination.operation.graph_id + ] gui_signal = SignalItem( component, dest_component, signal, parent=self ) diff --git a/b_asic/scheduler_gui/ui_main_window.py b/b_asic/scheduler_gui/ui_main_window.py index 3364516c..f42657c3 100644 --- a/b_asic/scheduler_gui/ui_main_window.py +++ b/b_asic/scheduler_gui/ui_main_window.py @@ -81,13 +81,19 @@ class Ui_MainWindow(object): self.info_table.setVerticalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() item.setTextAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignVCenter) + font = QtGui.QFont() + font.setBold(False) + font.setWeight(50) + item.setFont(font) self.info_table.setHorizontalHeaderItem(0, item) item = QtWidgets.QTableWidgetItem() item.setTextAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignVCenter) self.info_table.setHorizontalHeaderItem(1, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() - font.setBold(True) + font.setBold(False) + font.setWeight(50) + font.setKerning(True) item.setFont(font) brush = QtGui.QBrush(QtGui.QColor(160, 160, 164)) brush.setStyle(QtCore.Qt.SolidPattern) @@ -105,7 +111,8 @@ class Ui_MainWindow(object): self.info_table.setItem(0, 0, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() - font.setBold(True) + font.setBold(False) + font.setWeight(50) item.setFont(font) brush = QtGui.QBrush(QtGui.QColor(160, 160, 164)) brush.setStyle(QtCore.Qt.SolidPattern) @@ -138,6 +145,8 @@ class Ui_MainWindow(object): self.menu_Edit.setObjectName("menu_Edit") self.menuWindow = QtWidgets.QMenu(self.menubar) self.menuWindow.setObjectName("menuWindow") + self.menuHelp = QtWidgets.QMenu(self.menubar) + self.menuHelp.setObjectName("menuHelp") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") @@ -193,6 +202,12 @@ class Ui_MainWindow(object): icon = QtGui.QIcon.fromTheme("view-close") self.menu_close_schedule.setIcon(icon) self.menu_close_schedule.setObjectName("menu_close_schedule") + self.actionAbout = QtWidgets.QAction(MainWindow) + self.actionAbout.setObjectName("actionAbout") + self.actionDocumentation = QtWidgets.QAction(MainWindow) + self.actionDocumentation.setObjectName("actionDocumentation") + self.actionReorder = QtWidgets.QAction(MainWindow) + self.actionReorder.setObjectName("actionReorder") self.menuFile.addAction(self.menu_load_from_file) self.menuFile.addAction(self.menu_close_schedule) self.menuFile.addAction(self.menu_save) @@ -201,16 +216,21 @@ class Ui_MainWindow(object): self.menuFile.addAction(self.menu_quit) self.menuView.addAction(self.menu_node_info) self.menuWindow.addAction(self.menu_exit_dialog) + self.menuHelp.addAction(self.actionDocumentation) + self.menuHelp.addSeparator() + self.menuHelp.addAction(self.actionAbout) self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menu_Edit.menuAction()) self.menubar.addAction(self.menuView.menuAction()) self.menubar.addAction(self.menuWindow.menuAction()) + self.menubar.addAction(self.menuHelp.menuAction()) self.toolBar.addAction(self.menu_load_from_file) self.toolBar.addAction(self.menu_save) self.toolBar.addAction(self.menu_save_as) self.toolBar.addSeparator() self.toolBar.addAction(self.menu_node_info) self.toolBar.addAction(self.actionT) + self.toolBar.addAction(self.actionReorder) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) @@ -236,6 +256,7 @@ class Ui_MainWindow(object): self.menuView.setTitle(_translate("MainWindow", "&View")) self.menu_Edit.setTitle(_translate("MainWindow", "&Edit")) self.menuWindow.setTitle(_translate("MainWindow", "&Window")) + self.menuHelp.setTitle(_translate("MainWindow", "&Help")) self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) self.menu_load_from_file.setText( _translate("MainWindow", "&Load Schedule From File...") @@ -267,3 +288,11 @@ class Ui_MainWindow(object): self.menu_close_schedule.setText( _translate("MainWindow", "&Close Schedule") ) + self.actionAbout.setText(_translate("MainWindow", "About")) + self.actionDocumentation.setText( + _translate("MainWindow", "Documentation") + ) + self.actionReorder.setText(_translate("MainWindow", "Reorder")) + self.actionReorder.setToolTip( + _translate("MainWindow", "Reorder schedule based on start time") + ) -- GitLab