diff --git a/src/simudator/gui/breakpoint_window.py b/src/simudator/gui/breakpoint_window.py index 58b2a8306d5b8c18624a786d801b80d989746e02..bf713be52d56593502b42f2706a231868a7b6737 100644 --- a/src/simudator/gui/breakpoint_window.py +++ b/src/simudator/gui/breakpoint_window.py @@ -8,6 +8,7 @@ from PyQt5.QtWidgets import ( QWidget, ) +from simudator.core.breakpoint import Breakpoint from simudator.core.processor import Processor @@ -16,9 +17,12 @@ class BreakpointWindow(QWidget): def __init__(self, cpu: Processor): super().__init__() self.cpu = cpu + + # Make layouts layout = QHBoxLayout() button_layout = QVBoxLayout() + # Make buttons for editing breakpoints self.toggle_enabled_button = QPushButton("Toggle Enabled") self.toggle_enabled_button.clicked.connect(self.toggleEnabledClicked) button_layout.addWidget(self.toggle_enabled_button) @@ -26,22 +30,30 @@ class BreakpointWindow(QWidget): self.remove_button = QPushButton("Remove") self.remove_button.clicked.connect(self.removeItemClicked) button_layout.addWidget(self.remove_button) - layout.addLayout(button_layout) + # Add list of breakpoints self.breakpoint_list = QListWidget() - layout.addWidget(self.breakpoint_list) + self.setLayout(layout) + # Update so we fill the list with breakpoints self.update() - def update(self): + def update(self) -> None: + """ + Updates list with breakpoints by removing the current items + and then filling with new once according to the cpu's content. + """ + # Remove so we don't have dubble of all items self.removeAllItems() + # Get icons for showing if enabled i_icon = self.style().standardIcon(QStyle.SP_DialogApplyButton) stop_icon = self.style().standardIcon(QStyle.SP_DialogCancelButton) + # Add an list item for each breakpoint in cpu for bp_id, breakpoint in self.cpu.breakpoints.items(): bp_str = str(bp_id) + ": " + breakpoint.__str__() bp_item = QListWidgetItem(bp_str) @@ -53,11 +65,18 @@ class BreakpointWindow(QWidget): bp_item.setIcon(stop_icon) self.breakpoint_list.addItem(bp_item) - def removeAllItems(self): + def removeAllItems(self) -> None: + """ + Removes all list items from the breakpoint list. + """ for row in range(self.breakpoint_list.count()): self.breakpoint_list.takeItem(0) - def toggleEnabledClicked(self): + def toggleEnabledClicked(self) -> None: + """ + Called when the toggle enabled buttons is pressed. + Toggles the selected breakpoint so it is enabled or disabled. + """ current_bp = self.getSelectedItem() if current_bp is None: @@ -66,7 +85,11 @@ class BreakpointWindow(QWidget): current_bp.set_enabled(not current_bp.is_enabled) self.update() - def removeItemClicked(self): + def removeItemClicked(self) -> None: + """ + Called when the remove button is preseed. + Will remove the selected breakpoint from the cpu's breakpoint list. + """ bp_id = self.getSelectedItemId() # Do nothing if id is None @@ -76,7 +99,11 @@ class BreakpointWindow(QWidget): del self.cpu.breakpoints[bp_id] self.update() - def getSelectedItemId(self): + def getSelectedItemId(self) -> int: + """ + Gives the id of the selected breakpoint. + Used to fetch the corresponding breakpoint from the cpu. + """ current_bp_item = self.breakpoint_list.currentItem() # Return None if list is empty @@ -85,7 +112,10 @@ class BreakpointWindow(QWidget): return int(current_bp_item.text()[0]) - def getSelectedItem(self): + def getSelectedItem(self) -> Breakpoint: + """ + Returns the selected breakpoint. + """ bp_id = self.getSelectedItemId() # Do nothing if there are no breakpoints or if id is nonetype diff --git a/src/simudator/gui/gui.py b/src/simudator/gui/gui.py index ffb693716a845678c2eb9eff5270c6deea1139ee..3024c4eabc525432f61c92a25eb8503cf0d92d36 100644 --- a/src/simudator/gui/gui.py +++ b/src/simudator/gui/gui.py @@ -37,7 +37,7 @@ from simudator.gui.signal_graphics_item import SignalGraphicsItem class GUI(QMainWindow): """ This is the main class for the GUI. It handles creating the widnow for - the gui, aswell as the toolbat for controlling the simultaion. + the gui, aswell as the toolbar for controlling the simultaion. It takes a processor and visualizes its modules as boxes with the signals as lines between them. Graphics items for the modules and signals need to be created and added individually. @@ -54,7 +54,6 @@ class GUI(QMainWindow): self.setWindowTitle("SimuDator") self.cpu_graphics_scene = CpuGraphicsScene(cpu) self.graphics_view = QGraphicsView(self.cpu_graphics_scene) - #self.graphics_view.setDragMode(True) self.moduleActions: dict[str, QAction] = {} self.setCentralWidget(self.graphics_view) @@ -82,12 +81,9 @@ class GUI(QMainWindow): app.setStyleSheet("Qwidget.QMainWindow { background-color: yellow }") self.setAttribute(QtCore.Qt.WA_StyledBackground) - # Add this so we can save the window in scope later + # Add this so we can save the window in scope later, otherwise it disappears self.breakpoint_window = None - #"QGraphicsItem { background-color: blue }") - #self.setStyleSheet("background-color: yellow") - def createToolBar(self) -> None: """ Creates the toolbar containing the file, layout and toolbar @@ -253,10 +249,17 @@ class GUI(QMainWindow): } def updateCpuListeners(self) -> None: + """ + Updates the graphics items in the scene and the clock. + Used after the cpu has run or when the user has edited somehting. + """ self.cpu_graphics_scene.updateGraphicsItems() self.clock_label.setText("Clockcycle: " + str(self.cpu.get_clock())) def stateBreakpointDialog(self) -> None: + """ + Opens dialog window for user to create a breakpoint. + """ action = self.moduleActions['add_br'] module_name = action.data() module = self.cpu.get_module(module_name) @@ -264,11 +267,17 @@ class GUI(QMainWindow): state_br_dialog.okSignal.connect(self.addStateBreakpoint) def lambdaBreakpointDialog(self) -> None: + """ + Opens dialog window for user to create a breakpoint. + """ lambdas = self.cpu.get_breakpoint_lambdas() lambda_br_dialog = LambdaBreakpointDialog(lambdas, self) lambda_br_dialog.accepted.connect(self.addLambdaBreakpoint) def memoryBreakpointDialog(self) -> None: + """ + Opens dialog window for user to create a breakpoint. + """ action = self.moduleActions['add_mem_br'] module_name = action.data() module = self.cpu.get_module(module_name) @@ -276,10 +285,17 @@ class GUI(QMainWindow): memory_br_dialog.accepted.connect(self.addMemoryBreakpoint) def updateBreakpointWindow(self) -> None: + """ + Updates the breakpoint window when new breakpoints are added. + """ + # Don't do anything if window is closed if self.breakpoint_window is not None: self.breakpoint_window.update() def editModuleStateDialog(self) -> None: + """ + Opens dialog where user can edit the state of a module. + """ action = self.moduleActions['edit_state'] module_name = action.data() module = self.cpu.get_module(module_name) @@ -287,6 +303,9 @@ class GUI(QMainWindow): state_br_dialog.okSignal.connect(self.editModuleState) def editMemoryContentDialog(self) -> None: + """ + Opens dialog where user can edit the contents of a memory. + """ action = self.moduleActions['edit_memory'] module_name = action.data() module = self.cpu.get_module(module_name) @@ -377,6 +396,9 @@ class GUI(QMainWindow): self.undo_action.setDisabled(is_disable) def stepToolBarButtonClick(self): + """ + Runs the cpu a specified number of times according to the jump value box. + """ # Don't do steps if cpu is running if self.cpu_running: @@ -394,6 +416,9 @@ class GUI(QMainWindow): self.updateCpuListeners() def runToolBarButtonClick(self) -> None: + """ + Runs the cpu until it is stopped by user input, breakpoint or similar. + """ # Don't run if already running if self.cpu_running: @@ -408,33 +433,27 @@ class GUI(QMainWindow): @pyqtSlot(int) def cpuHaltedFunction(self, steps: int) -> None: + """ + Called from other thread when cpu has halted. + Will inform the user and update visuals. + """ + # If a breakpoint halted the program inform thr user + if self.cpu.breakpoint_reached: + self.messageBox("Reached breakpoint: " + self.cpu.last_breakpoint.__str__()) # Only show halted message for larger steps that take time # This is done so a user dosent have to close # the message box after every small step - if self.cpu.breakpoint_reached: - self.messageBox("Reached breakpoint: " + self.cpu.last_breakpoint.__str__()) elif steps > self.HALT_MESSAGE_THRESHOLD: self.messageBox("The processor halted.") self.updateCpuListeners() self.cpu_running = False self.setDisabledWhenRunning(False) - def stepNToolBarButtonClick(self) -> None: - cycles, ok = QInputDialog(self).getInt(self, - "Input number of cycles to run", - "Input number of cycles to run", - ) - if ok: - if cycles < 1: - self.errorBox("Please input a number larger than 0.") - return - self.cpu.unstop() - for _ in range(cycles): - self.cpu.do_tick() - self.updateCpuListeners() - def stopToolBarButtonClick(self) -> None: + """ + Tells the cpu to stop. It will then stop at an appropriate in its own thread. + """ self.cpu.stop() self.updateCpuListeners() @@ -690,18 +709,30 @@ class GUI(QMainWindow): self.cpu.save_state_to_file(path) def openBreakpointWindow(self): + """ + Opens window for editing breakpoints. + """ self.breakpoint_window = BreakpointWindow(self.cpu) self.breakpoint_window.show() def showPortNamesBarButtonClick(self): + """ + Toggles showing port names in the graphics scene. + """ self.cpu_graphics_scene.setPortNamesVisibility( self.port_vis_action.isChecked()) def showSignalsMenuButtonClick(self): + """ + Toggle shoing the signals in the graphics scene. + """ self.cpu_graphics_scene.setAllSignalsVisibility( self.signal_vis_action.isChecked()) def toggleLayoutLockMenuButtonClick(self): + """ + Toggles so the layout can not be edited. + """ self.cpu_graphics_scene.setLayoutLock( self.lock_layout_action.isChecked()) @@ -762,10 +793,16 @@ class GUI(QMainWindow): self.errorBox("Unable to undo the cycle.") def addModuleGraphicsItem(self, graphics_item: ModuleGraphicsItem): + """ + Adds an item to the graphics scene. + """ self.cpu_graphics_scene.addModuleGraphicsItem(graphics_item) graphics_item.addActions(self.moduleActions.values()) def addAllSignals(self) -> None: + """ + Add signals depending on modules in the graphics scene. + """ self.cpu_graphics_scene.addAllSignals() diff --git a/src/simudator/gui/orientation.py b/src/simudator/gui/orientation.py index e3f14c4516c9e864747d17e68477f377b88ecc31..fed1d171ca6d5c80d52dc585b35d46a7128cbd5a 100644 --- a/src/simudator/gui/orientation.py +++ b/src/simudator/gui/orientation.py @@ -2,6 +2,9 @@ from enum import IntEnum class Orientation(IntEnum): + """ + Used to give and track ports orientations. + """ UP = 0 LEFT = 1 DOWN = 2 diff --git a/src/simudator/gui/port_graphics_item.py b/src/simudator/gui/port_graphics_item.py index 095d24f15f114d8563efb2b8d01087da76a883fd..43856d2ab12c32587fb7d16934aeb29f3bdbe786 100644 --- a/src/simudator/gui/port_graphics_item.py +++ b/src/simudator/gui/port_graphics_item.py @@ -30,6 +30,12 @@ class PyQtSignalHolder(QObject): class PortGraphicsItem(QGraphicsItemGroup): + """ + Graphics item that visualises the connection between a module and a signal. + Does this by drawing a small line that goes out from the + module and the name of the signal. The port can be moved around + the edge of the of the module grapics item. + """ LINE_LENGTH = 8 CHAR_HEIGHT = 18 @@ -71,6 +77,9 @@ class PortGraphicsItem(QGraphicsItemGroup): self.setZValue(1.0) def drawPort(self) -> None: + """ + Draws the port as a small line and a label with the signals name. + """ # Create a horizontal and vertical line # We create two so we dont have to delete them when changing orientation @@ -89,6 +98,10 @@ class PortGraphicsItem(QGraphicsItemGroup): self.addToGroup(self.name_label) def setOrientation(self, orientation: int) -> None: + """ + Sets the ports orientation by showing the vertical or horizontal line + and moving the name label appropriatly. + """ self.orientation = orientation if orientation == Orientation.UP: @@ -172,6 +185,10 @@ class PortGraphicsItem(QGraphicsItemGroup): event.accept() def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None: + """ + Moves the port if left click is held. Will move it around + the edge of it parent modules baserect. + """ if not self.left_click and self.parent is not None: return if self.isLocked: @@ -235,6 +252,7 @@ class PortGraphicsItem(QGraphicsItemGroup): menu.exec_(event.screenPos()) event.accept() + # TODO: Need comments here of what does what def toggleVisibility(self) -> None: self.setVisible(not self.isVisible()) self.toggled.emit() diff --git a/src/simudator/gui/run_continuously_thread.py b/src/simudator/gui/run_continuously_thread.py index 225513638f30b1271b2fc1969360f9d107cd4ec5..1c5dfd32e4979cfd5cee0501a9b35bacc993005e 100644 --- a/src/simudator/gui/run_continuously_thread.py +++ b/src/simudator/gui/run_continuously_thread.py @@ -2,6 +2,12 @@ from PyQt5.QtCore import QRunnable class RunThread(QRunnable): + """ + This class is used to run the cpu on a seperate thread. + This allows the user to interact with the GUI will th cpu is running. + When the cpu halts this thread will emit to it's given signal + the GUI can then handel what should happend after execution on its own. + """ def __init__(self, cpu, signal, run_continuously=True, steps=0): super().__init__()