diff --git a/b_asic/GUI/drag_button.py b/b_asic/GUI/drag_button.py index 7ed8c36119945460e9253e24ba493c570e6aa8de..638a04aaf0fdc738356d27c7450987e96dcf8c57 100644 --- a/b_asic/GUI/drag_button.py +++ b/b_asic/GUI/drag_button.py @@ -50,7 +50,7 @@ class DragButton(QPushButton): window, parent=None, ): - self.name = operation.graph_id + self.name = operation.name or operation.graph_id self._ports: List[PortButton] = [] self.show_name = show_name self._window = window @@ -88,6 +88,10 @@ class DragButton(QPushButton): self._properties_window = PropertiesWindow(self, self._window) self._properties_window.show() + def type_name(self): + """Return the type name of the underlying operation.""" + return self.operation.type_name() + def add_label(self, label: str) -> None: """ Add label to button. diff --git a/b_asic/GUI/gui_interface.py b/b_asic/GUI/gui_interface.py index d7f19149a65751edad231e38109401aee354f943..c087ee1ef038225cdcd8e1f5fd9487c48918a918 100644 --- a/b_asic/GUI/gui_interface.py +++ b/b_asic/GUI/gui_interface.py @@ -107,19 +107,20 @@ class Ui_main_window(object): self.recent_sfg.setObjectName("recent_sfg") self.exit_menu = QtWidgets.QAction(main_window) self.exit_menu.setObjectName("exit_menu") + self.select_all = QtWidgets.QAction(main_window) + self.select_all.setObjectName("select_all") + self.unselect_all = QtWidgets.QAction(main_window) + self.unselect_all.setObjectName("unselect_all") self.actionSimulateSFG = QtWidgets.QAction(main_window) self.actionSimulateSFG.setObjectName("actionSimulateSFG") self.actionShowPC = QtWidgets.QAction(main_window) self.actionShowPC.setObjectName("actionShowPC") - self.aboutBASIC = QtWidgets.QAction(main_window) - self.aboutBASIC.setObjectName("aboutBASIC") self.faqBASIC = QtWidgets.QAction(main_window) self.faqBASIC.setObjectName("faqBASIC") self.keybindsBASIC = QtWidgets.QAction(main_window) self.keybindsBASIC.setObjectName("keybindsBASIC") - self.actionToolbar = QtWidgets.QAction(main_window) - self.actionToolbar.setCheckable(True) - self.actionToolbar.setObjectName("actionToolbar") + self.aboutBASIC = QtWidgets.QAction(main_window) + self.aboutBASIC.setObjectName("aboutBASIC") self.file_menu.addAction(self.load_menu) self.file_menu.addAction(self.save_menu) self.file_menu.addAction(self.load_operations) @@ -127,12 +128,14 @@ class Ui_main_window(object): self.file_menu.addMenu(self.recent_sfg) self.file_menu.addSeparator() self.file_menu.addAction(self.exit_menu) - self.view_menu.addAction(self.actionToolbar) + self.edit_menu.addAction(self.select_all) + self.edit_menu.addAction(self.unselect_all) self.run_menu.addAction(self.actionShowPC) self.run_menu.addAction(self.actionSimulateSFG) - self.help_menu.addAction(self.aboutBASIC) self.help_menu.addAction(self.faqBASIC) self.help_menu.addAction(self.keybindsBASIC) + self.help_menu.addSeparator() + self.help_menu.addAction(self.aboutBASIC) self.menu_bar.addAction(self.file_menu.menuAction()) self.menu_bar.addAction(self.edit_menu.menuAction()) self.menu_bar.addAction(self.view_menu.menuAction()) @@ -170,23 +173,24 @@ class Ui_main_window(object): self.operation_list.indexOf(self.custom_operations_page), _translate("main_window", "Custom operation"), ) - self.file_menu.setTitle(_translate("main_window", "File")) - self.edit_menu.setTitle(_translate("main_window", "Edit")) - self.view_menu.setTitle(_translate("main_window", "View")) - self.run_menu.setTitle(_translate("main_window", "Run")) - self.actionShowPC.setText(_translate("main_window", "Show PG")) - self.help_menu.setTitle(_translate("main_window", "Help")) - self.actionSimulateSFG.setText(_translate("main_window", "Simulate SFG")) - self.aboutBASIC.setText(_translate("main_window", "About B-ASIC")) - self.faqBASIC.setText(_translate("main_window", "FAQ")) - self.keybindsBASIC.setText(_translate("main_window", "Keybinds")) - self.load_menu.setText(_translate("main_window", "Load SFG")) - self.save_menu.setText(_translate("main_window", "Save SFG")) - self.load_operations.setText(_translate("main_window", "Load operations")) - self.recent_sfg.setTitle(_translate("main_window", "Recent SFG")) - self.exit_menu.setText(_translate("main_window", "Exit")) + self.file_menu.setTitle(_translate("main_window", "&File")) + self.edit_menu.setTitle(_translate("main_window", "&Edit")) + self.view_menu.setTitle(_translate("main_window", "&View")) + self.run_menu.setTitle(_translate("main_window", "&Run")) + self.actionShowPC.setText(_translate("main_window", "Show &precedence graph")) + self.help_menu.setTitle(_translate("main_window", "&Help")) + self.select_all.setText(_translate("main_window", "Select &all")) + self.unselect_all.setText(_translate("main_window", "&Unselect all")) + self.actionSimulateSFG.setText(_translate("main_window", "&Simulate SFG")) + self.aboutBASIC.setText(_translate("main_window", "&About B-ASIC")) + self.faqBASIC.setText(_translate("main_window", "&FAQ")) + self.keybindsBASIC.setText(_translate("main_window", "&Keybinds")) + self.load_menu.setText(_translate("main_window", "&Load SFG")) + self.save_menu.setText(_translate("main_window", "&Save SFG")) + self.load_operations.setText(_translate("main_window", "Load &operations")) + self.recent_sfg.setTitle(_translate("main_window", "&Recent SFG")) + self.exit_menu.setText(_translate("main_window", "E&xit")) self.exit_menu.setShortcut(_translate("main_window", "Ctrl+Q")) - self.actionToolbar.setText(_translate("main_window", "Toolbar")) if __name__ == "__main__": diff --git a/b_asic/GUI/main_window.py b/b_asic/GUI/main_window.py index 6838c9f8b3bf2a35bcf8732b5d8cdbdf59c35ebf..0a7c19b84817ee61f82644d977ee9c687063951f 100644 --- a/b_asic/GUI/main_window.py +++ b/b_asic/GUI/main_window.py @@ -70,6 +70,8 @@ QCoreApplication.setApplicationVersion(__version__) class SFGMainWindow(QMainWindow): def __init__(self): super().__init__() + self._logger = logging.getLogger(__name__) + self._window = self self._ui = Ui_main_window() self._ui.setupUi(self) self.setWindowIcon(QIcon("small_logo.png")) @@ -86,8 +88,6 @@ class SFGMainWindow(QMainWindow): self._operation_to_sfg: Dict[DragButton, SFG] = {} self._pressed_ports: List[PortButton] = [] self._sfg_dict: Dict[str, SFG] = {} - self._window = self - self._logger = logging.getLogger(__name__) self._plot: Dict[Simulation, PlotWindow] = {} self._ports: Dict[DragButton, List[PortButton]] = {} @@ -99,8 +99,11 @@ class SFGMainWindow(QMainWindow): ) self._graphics_view.setDragMode(QGraphicsView.RubberBandDrag) - # Create _toolbar + # Create toolbar self._toolbar = self.addToolBar("Toolbar") + self._toolbar.addAction(get_icon('open'), "Load SFG", self.load_work) + self._toolbar.addAction(get_icon('save'), "Save SFG", self.save_work) + self._toolbar.addSeparator() self._toolbar.addAction( get_icon('new-sfg'), "Create SFG", self.create_sfg_from_toolbar ) @@ -133,17 +136,11 @@ class SFGMainWindow(QMainWindow): self._scene.selectionChanged.connect(self._select_operations) self.move_button_index = 0 - self._show_names = True - - self._check_show_names = QAction("Show operation names") - self._check_show_names.triggered.connect(self.view_operation_names) - self._check_show_names.setCheckable(True) - self._check_show_names.setChecked(True) - self._ui.view_menu.addAction(self._check_show_names) self._ui.actionShowPC.triggered.connect(self._show_precedence_graph) self._ui.actionSimulateSFG.triggered.connect(self.simulate_sfg) self._ui.faqBASIC.triggered.connect(self.display_faq_page) + self._ui.faqBASIC.setShortcut(QKeySequence("Ctrl+?")) self._ui.aboutBASIC.triggered.connect(self.display_about_page) self._ui.keybindsBASIC.triggered.connect(self.display_keybindings_page) self._ui.core_operations_list.itemClicked.connect( @@ -157,20 +154,42 @@ class SFGMainWindow(QMainWindow): ) self._ui.save_menu.triggered.connect(self.save_work) self._ui.save_menu.setIcon(get_icon('save')) + self._ui.save_menu.setShortcut(QKeySequence("Ctrl+S")) self._ui.load_menu.triggered.connect(self.load_work) self._ui.load_menu.setIcon(get_icon('open')) + self._ui.load_menu.setShortcut(QKeySequence("Ctrl+O")) self._ui.load_operations.triggered.connect(self.add_namespace) self._ui.exit_menu.triggered.connect(self.exit_app) - self._shortcut_open = QShortcut(QKeySequence("Ctrl+O"), self) - self._shortcut_open.activated.connect(self.load_work) - self._shortcut_save = QShortcut(QKeySequence("Ctrl+S"), self) - self._shortcut_save.activated.connect(self.save_work) - self._shortcut_help = QShortcut(QKeySequence("Ctrl+?"), self) - self._shortcut_help.activated.connect(self.display_faq_page) + self._ui.select_all.triggered.connect(self._select_all) + self._ui.select_all.setShortcut(QKeySequence("Ctrl+A")) + self._ui.unselect_all.triggered.connect(self._unselect_all) self._shortcut_signal = QShortcut(QKeySequence(Qt.Key_Space), self) self._shortcut_signal.activated.connect(self._connect_callback) self._create_recent_file_actions_and_menus() + # View menu + + # Operation names + self._show_names = True + self._check_show_names = QAction("&Operation names") + self._check_show_names.triggered.connect(self.view_operation_names) + self._check_show_names.setCheckable(True) + self._check_show_names.setChecked(True) + self._ui.view_menu.addAction(self._check_show_names) + + self._ui.view_menu.addSeparator() + + # Toggle toolbar + self._ui.view_menu.addAction(self._toolbar.toggleViewAction()) + + # Toggle status bar + self._statusbar_visible = QAction("&Status bar") + self._statusbar_visible.setCheckable(True) + self._statusbar_visible.setChecked(True) + self._statusbar_visible.triggered.connect(self._toggle_statusbar) + self._ui.view_menu.addAction(self._statusbar_visible) + + # Non-modal dialogs self._keybindings_page = None self._about_page = None self._faq_page = None @@ -201,17 +220,16 @@ class SFGMainWindow(QMainWindow): self._graphics_view.scale(self._zoom, self._zoom) self._zoom = old_zoom - def view_operation_names(self) -> None: - if self._check_show_names.isChecked(): - self._show_names = True - else: - self._show_names = False + def view_operation_names(self, event=None) -> None: + self._show_names = self._check_show_names.isChecked() for operation in self._drag_operation_scenes: operation.label.setOpacity(self._show_names) operation.show_name = self._show_names def _save_work(self) -> None: + if not self.sfg_widget.sfg: + self.update_statusbar("No SFG selected - saving cancelled") sfg = cast(SFG, self.sfg_widget.sfg) file_dialog = QFileDialog() file_dialog.setDefaultSuffix(".py") @@ -240,8 +258,12 @@ class SFGMainWindow(QMainWindow): return self._logger.info("Saved SFG to path: " + str(module)) + self.update_statusbar("Saved SFG to path: " + str(module)) def save_work(self, event=None) -> None: + if not self._sfg_dict: + self.update_statusbar("No SFG to save") + return self.sfg_widget = SelectSFGWindow(self) self.sfg_widget.show() @@ -524,10 +546,16 @@ class SFGMainWindow(QMainWindow): self._sfg_dict[sfg.name] = sfg def _show_precedence_graph(self, event=None) -> None: + if not self._sfg_dict: + self.update_statusbar("No SFG to show") + return self._precedence_graph_dialog = PrecedenceGraphWindow(self) self._precedence_graph_dialog.add_sfg_to_dialog() self._precedence_graph_dialog.show() + def _toggle_statusbar(self, event=None) -> None: + self._statusbar.setVisible(self._statusbar_visible.isChecked()) + def get_operations_from_namespace(self, namespace) -> List[str]: self._logger.info( "Fetching operations from namespace: " + str(namespace.__name__) @@ -776,6 +804,24 @@ class SFGMainWindow(QMainWindow): self._pressed_operations = selected + def _select_all(self, event=None) -> None: + if not self._drag_buttons: + self.update_statusbar("No operations to select") + return + + for operation in self._drag_buttons.values(): + operation._toggle_button(pressed=False) + self.update_statusbar("Selected all operations") + + def _unselect_all(self, event=None) -> None: + if not self._drag_buttons: + self.update_statusbar("No operations to unselect") + return + + for operation in self._drag_buttons.values(): + operation._toggle_button(pressed=True) + self.update_statusbar("Unselected all operations") + def _simulate_sfg(self) -> None: self._thread = dict() self._sim_worker = dict() @@ -796,6 +842,10 @@ class SFGMainWindow(QMainWindow): self._plot[sim].show() def simulate_sfg(self, event=None) -> None: + if not self._sfg_dict: + self.update_statusbar("No SFG to simulate") + return + self._simulation_dialog = SimulateSFGWindow(self) for _, sfg in self._sfg_dict.items(): diff --git a/b_asic/GUI/precedence_graph_window.py b/b_asic/GUI/precedence_graph_window.py index 500862be07de77cf5638e217337975288fc2d4f5..82f5ca5a88dd8e045347d8e63a9d112859f78879 100644 --- a/b_asic/GUI/precedence_graph_window.py +++ b/b_asic/GUI/precedence_graph_window.py @@ -23,12 +23,16 @@ class PrecedenceGraphWindow(QDialog): self.setWindowTitle("Show precedence graph") self._dialog_layout = QVBoxLayout() - self._show_precedence_graph_button = QPushButton("Show PG") + self._show_precedence_graph_button = QPushButton("Show precedence graph") self._show_precedence_graph_button.clicked.connect(self.show_precedence_graph) self._dialog_layout.addWidget(self._show_precedence_graph_button) self.setLayout(self._dialog_layout) def add_sfg_to_dialog(self): + if not self._window._sfg_dict: + self.accept() + self.pc.emit() + self._sfg_layout = QVBoxLayout() self._options_layout = QFormLayout() @@ -45,6 +49,9 @@ class PrecedenceGraphWindow(QDialog): self._dialog_layout.addWidget(frame) self._dialog_layout.addLayout(self._sfg_layout) + if len(self._check_box_dict) == 1: + check_box = list(self._check_box_dict.keys())[0] + check_box.setChecked(True) def show_precedence_graph(self): for check_box, sfg in self._check_box_dict.items(): diff --git a/b_asic/GUI/select_sfg_window.py b/b_asic/GUI/select_sfg_window.py index 483e978b474169e77516f6b851a1c4164b1a29c1..53118a4b469574ab35db4f95536e9b72e187316f 100644 --- a/b_asic/GUI/select_sfg_window.py +++ b/b_asic/GUI/select_sfg_window.py @@ -33,6 +33,12 @@ class SelectSFGWindow(QDialog): self._sfg_combo_box.addItem(sfg) self._dialog_layout.addWidget(self._sfg_combo_box) + if len(self._window._sfg_dict) == 1: + self._sfg_combo_box.setCurrentIndex(0) + self.save_properties() + if not self._window._sfg_dict: + self.accept() + self.ok.emit() def save_properties(self): self.sfg = self._window._sfg_dict[self._sfg_combo_box.currentText()] diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py index c798f72ac358ef4363028f942e23a67c63f49d85..a21c7d6f368f77c9b72708611b0c2cfd441309a7 100644 --- a/b_asic/scheduler_gui/main_window.py +++ b/b_asic/scheduler_gui/main_window.py @@ -206,15 +206,19 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): 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: factor, ok = QInputDialog.getInt( self, "Increase time resolution", "Factor", 1, 1 ) - if ok: + if ok and factor > 1: self.schedule.increase_time_resolution(factor) self.open(self.schedule) + self.update_statusbar(f"Time resolution increased by a factor {factor}") + if not ok: + self.update_statusbar("Cancelled") @Slot() def _decrease_time_resolution(self) -> None: @@ -222,9 +226,12 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): factor, ok = QInputDialog.getItem( self, "Decrease time resolution", "Factor", vals, editable=False ) - if ok: + if ok and int(factor) > 1: self.schedule.decrease_time_resolution(int(factor)) self.open(self.schedule) + self.update_statusbar(f"Time resolution decreased by a factor {factor}") + if not ok: + self.update_statusbar("Cancelled") def wheelEvent(self, event) -> None: """Zoom in or out using mouse wheel if control is pressed.""" @@ -356,6 +363,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow): del self._schedule self._schedule = None self.info_table_clear() + self.update_statusbar("Closed schedule") @Slot() def save(self) -> None: