diff --git a/opencodeblocks/blocks/codeblock.py b/opencodeblocks/blocks/codeblock.py index ae8c9fc1..87619798 100644 --- a/opencodeblocks/blocks/codeblock.py +++ b/opencodeblocks/blocks/codeblock.py @@ -82,14 +82,16 @@ def init_run_button(self): """Initialize the run button""" run_button = QPushButton(">", self.root) run_button.move(int(self.edge_size), int(self.edge_size / 2)) - run_button.setFixedSize(int(3 * self.edge_size), int(3 * self.edge_size)) + run_button.setFixedSize(int(3 * self.edge_size), + int(3 * self.edge_size)) run_button.clicked.connect(self.run_left) return run_button def init_run_all_button(self): """Initialize the run all button""" run_all_button = QPushButton(">>", self.root) - run_all_button.setFixedSize(int(3 * self.edge_size), int(3 * self.edge_size)) + run_all_button.setFixedSize( + int(3 * self.edge_size), int(3 * self.edge_size)) run_all_button.clicked.connect(self.run_right) run_all_button.raise_() @@ -132,10 +134,26 @@ def has_output(self) -> bool: return True return False + def _interrupt_execution(self): + """ Interrupt an execution, reset the blocks in the queue """ + for block, _ in self.source_editor.kernel.execution_queue: + # Reset the blocks that have not been run + block.reset_buttons() + block.has_been_run = False + # Clear the queue + self.source_editor.kernel.execution_queue = [] + # Interrupt the kernel + self.source_editor.kernel.kernel_manager.interrupt_kernel() + def run_left(self, in_right_button=False): """ Run all of the block's dependencies and then run the block """ + # If the user presses left run when running, cancel the execution + if self.run_button.text() == "..." and not in_right_button: + self._interrupt_execution() + return + # If no dependencies if not self.has_input(): return self.run_code() @@ -164,6 +182,11 @@ def run_left(self, in_right_button=False): def run_right(self): """Run all of the output blocks and all their dependencies""" + # If the user presses right run when running, cancel the execution + if self.run_all_button.text() == "...": + self._interrupt_execution() + return + # If no output, run left if not self.has_output(): return self.run_left(in_right_button=True) @@ -171,10 +194,15 @@ def run_right(self): # Same as run_left but instead of running the blocks, we'll use run_left graph = self.scene().create_graph() edges = bfs_edges(graph, self) - blocks_to_run: List["OCBCodeBlock"] = [self] + [v for _, v in edges] + blocks_to_run: List["OCBCodeBlock"] = [ + self] + [v for _, v in edges] for block in blocks_to_run[::-1]: block.run_left(in_right_button=True) + def reset_has_been_run(self): + """ Reset has_been_run, is called when the output is an error """ + self.has_been_run = False + def update_title(self): """Change the geometry of the title widget""" self.title_widget.setGeometry( diff --git a/opencodeblocks/graphics/kernel.py b/opencodeblocks/graphics/kernel.py index 7563d9fa..a4c4fcfe 100644 --- a/opencodeblocks/graphics/kernel.py +++ b/opencodeblocks/graphics/kernel.py @@ -43,7 +43,7 @@ def message_to_output(self, message: dict) -> Tuple[str, str]: # output a print (print("Hello World")) out = message['text'] elif 'traceback' in message: - message_type = 'text' + message_type = 'error' # output an error out = '\n'.join(message['traceback']) else: @@ -65,6 +65,7 @@ def run_block(self, block, code: str): worker.signals.image.connect(block.handle_image) worker.signals.finished.connect(self.run_queue) worker.signals.finished_block.connect(block.reset_buttons) + worker.signals.error.connect(block.reset_has_been_run) block.source_editor.threadpool.start(worker) def run_queue(self): diff --git a/opencodeblocks/graphics/worker.py b/opencodeblocks/graphics/worker.py index 93a3b5d2..abb6185a 100644 --- a/opencodeblocks/graphics/worker.py +++ b/opencodeblocks/graphics/worker.py @@ -13,6 +13,7 @@ class WorkerSignals(QObject): image = pyqtSignal(str) finished = pyqtSignal() finished_block = pyqtSignal() + error = pyqtSignal() class Worker(QRunnable): @@ -40,6 +41,8 @@ async def run_code(self): self.signals.stdout.emit(output) elif output_type == 'image': self.signals.image.emit(output) + elif output_type == 'error': + self.signals.error.emit() self.signals.finished.emit() self.signals.finished_block.emit()