diff --git a/README.md b/README.md index 39d9539e..5daf4891 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ OpenCodeBlocks is an open-source tool for modular visual programing in python. +![](media/mnist_example.gif) + ## Features * Put your python code in small independent blocks diff --git a/media/mnist_example.gif b/media/mnist_example.gif new file mode 100644 index 00000000..0f74de8b Binary files /dev/null and b/media/mnist_example.gif differ diff --git a/opencodeblocks/graphics/blocks/codeblock.py b/opencodeblocks/graphics/blocks/codeblock.py index ef088285..bcadca5b 100644 --- a/opencodeblocks/graphics/blocks/codeblock.py +++ b/opencodeblocks/graphics/blocks/codeblock.py @@ -3,16 +3,19 @@ """ Module for the base OCB Code Block. """ +import re from typing import Optional -from PyQt5.QtCore import Qt, QByteArray, QPointF +from PyQt5.QtCore import QCoreApplication, Qt, QByteArray, QPointF from PyQt5.QtGui import QPainter, QPainterPath, QPixmap from PyQt5.QtWidgets import QStyleOptionGraphicsItem, QWidget, QGraphicsProxyWidget, QLabel, \ - QGraphicsSceneMouseEvent, QApplication + QGraphicsSceneMouseEvent, QApplication, QPushButton from opencodeblocks.graphics.blocks.block import OCBBlock from opencodeblocks.graphics.pyeditor import PythonEditor +ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + class OCBCodeBlock(OCBBlock): """ @@ -34,6 +37,7 @@ def __init__(self, **kwargs): self.source_editor = self.init_source_editor() self.display = self.init_display() + self.run_button = self.init_run_button() self.stdout = "" self.image = "" @@ -75,6 +79,13 @@ def update_all(self): int(self.width - 2*self.edge_size), int(self.output_panel_height) ) + run_button_widget = self.run_button.widget() + run_button_widget.setGeometry( + int(self.edge_size), + int(self.edge_size + self.title_height), + int(2.5*self.edge_size), + int(2.5*self.edge_size) + ) super().update_all() @property @@ -100,7 +111,11 @@ def stdout(self, value:str): # If there is a text output, erase the image output and display the text output self.image = "" editor_widget = self.display.widget() - editor_widget.setText(self._stdout) + # Remove ANSI color codes + text = ansi_escape.sub('', value) + # Remove backspaces (tf loading bars) + text = text.replace('\x08', '') + editor_widget.setText(text) @property def image(self) -> str: @@ -171,7 +186,7 @@ def _stop_resize(self): QApplication.restoreOverrideCursor() def mouseMoveEvent(self, event:QGraphicsSceneMouseEvent): - """ + """ We override the default resizing behavior as the code part and the display part of the block block can be resized independently. """ @@ -206,3 +221,32 @@ def init_display(self): display_graphics.setWidget(display) display_graphics.setZValue(-1) return display_graphics + + def init_run_button(self): + """ Initialize the run button """ + run_button_graphics = QGraphicsProxyWidget(self) + run_button = QPushButton(">") + run_button.setMinimumWidth(self.edge_size) + run_button_graphics.setWidget(run_button) + run_button.clicked.connect(self.run_code) + return run_button_graphics + + def run_code(self): + """Run the code in the block""" + code = self.source_editor.widget().text() + kernel = self.source_editor.widget().kernel + self.source = code + # Execute the code + kernel.client.execute(code) + done = False + # While the kernel sends messages + while done is False: + # Keep the GUI alive + QCoreApplication.processEvents() + # Save kernel message and display it + output, output_type, done = kernel.update_output() + if done is False: + if output_type == 'text': + self.stdout = output + elif output_type == 'image': + self.image = output diff --git a/opencodeblocks/graphics/pyeditor.py b/opencodeblocks/graphics/pyeditor.py index cddafa16..1770852d 100644 --- a/opencodeblocks/graphics/pyeditor.py +++ b/opencodeblocks/graphics/pyeditor.py @@ -4,7 +4,7 @@ """ Module for OCB in block python editor. """ from typing import TYPE_CHECKING, List -from PyQt5.QtCore import Qt, QCoreApplication +from PyQt5.QtCore import Qt from PyQt5.QtGui import QFocusEvent, QFont, QFontMetrics, QColor from PyQt5.Qsci import QsciScintilla, QsciLexerPython from opencodeblocks.graphics.theme_manager import theme_manager @@ -31,6 +31,7 @@ def __init__(self, block: OCBBlock): """ super().__init__(None) self.block = block + self.kernel = kernel self.setText(self.block.source) self.update_theme() @@ -101,28 +102,4 @@ def focusInEvent(self, event: QFocusEvent): def focusOutEvent(self, event: QFocusEvent): """ PythonEditor reaction to PyQt focusOut events. """ self.set_views_mode("MODE_NOOP") - - code = self.text() - if self.isModified() and code != self.block.source: - self.block.source = code - self.setModified(False) - # Execute the code - kernel.client.execute(code) - done = False - # While the kernel sends messages - while done is False: - # Keep the GUI alive - QCoreApplication.processEvents() - # Save kernel message and display it - output, output_type, done = kernel.update_output() - if done is False: - if output_type == 'text': - self.block.stdout = output - elif output_type == 'image': - self.block.image = output return super().focusOutEvent(event) - - def gatherBlockInputs(self): - args = [2, 3] - kwargs = {"chicken": False} - return args, kwargs