From 6ee64650746b9698dac4d3b7620e22a9e66e2a4e Mon Sep 17 00:00:00 2001 From: AlexandreSajus Date: Thu, 18 Nov 2021 15:51:33 +0100 Subject: [PATCH 1/4] :sparkles: autopep8 --- main.py | 2 +- mnist.ipyg | 401 ++++++++++++++++++ opencodeblocks/core/serializable.py | 2 +- opencodeblocks/graphics/blocks/codeblock.py | 47 +- opencodeblocks/graphics/function_parsing.py | 1 + opencodeblocks/graphics/pyeditor.py | 5 +- opencodeblocks/graphics/qss/__init__.py | 3 +- opencodeblocks/graphics/qss/dark_resources.py | 7 +- opencodeblocks/graphics/theme.py | 1 + opencodeblocks/graphics/theme_manager.py | 9 +- opencodeblocks/graphics/widget.py | 6 +- opencodeblocks/graphics/window.py | 81 ++-- pylint_score.py | 5 +- tests/integration/test_blocks.py | 20 +- tests/integration/test_window.py | 3 +- utils.py | 2 + 16 files changed, 513 insertions(+), 82 deletions(-) create mode 100644 mnist.ipyg diff --git a/main.py b/main.py index 54ac941f..4915304b 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ from qtpy.QtWidgets import QApplication from opencodeblocks.graphics.window import OCBWindow -sys.path.insert(0, os.path.join( os.path.dirname(__file__), "..", ".." )) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..")) if __name__ == '__main__': app = QApplication(sys.argv) diff --git a/mnist.ipyg b/mnist.ipyg new file mode 100644 index 00000000..b24ec8d2 --- /dev/null +++ b/mnist.ipyg @@ -0,0 +1,401 @@ +{ + "id": 2205665405400, + "blocks": [ + { + "id": 2443477874008, + "title": "Model Train", + "block_type": "code", + "source": "model.fit(x=x_train,y=y_train, epochs=10)\r\n\r\n", + "position": [ + 2022.4804687499986, + -313.02343749999983 + ], + "width": 641.5, + "height": 223.25, + "metadata": { + "title_metadata": { + "color": "white", + "font": "Ubuntu", + "size": 10, + "padding": 4.0 + } + }, + "sockets": [ + { + "id": 2443477875016, + "type": "input", + "position": [ + 0.0, + 42.0 + ], + "metadata": { + "color": "#e02c2c", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + }, + { + "id": 2443477875160, + "type": "output", + "position": [ + 641.5, + 42.0 + ], + "metadata": { + "color": "#35bc31", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + } + ] + }, + { + "id": 2443477924600, + "title": "Keras Model Predict", + "block_type": "code", + "source": "prediction = np.argmax(model.predict(x_test[9].reshape(1, 28, 28, 1)))\r\nplt.imshow(x_test[9], cmap='gray')\r\nplt.title(\"Predicted: \" + str(prediction))", + "position": [ + 2771.796874999998, + -145.09374999999983 + ], + "width": 627.1875, + "height": 479.3749999999999, + "metadata": { + "title_metadata": { + "color": "white", + "font": "Ubuntu", + "size": 10, + "padding": 4.0 + } + }, + "sockets": [ + { + "id": 2443477925608, + "type": "input", + "position": [ + 0.0, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + }, + { + "id": 2443477925752, + "type": "output", + "position": [ + 627.1875, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + } + ] + }, + { + "id": 2443477997032, + "title": "Keras Model eval", + "block_type": "code", + "source": "model.evaluate(x_test, y_test)\r\n", + "position": [ + 2779.039062499998, + -401.77343749999955 + ], + "width": 647.75, + "height": 221.75, + "metadata": { + "title_metadata": { + "color": "white", + "font": "Ubuntu", + "size": 10, + "padding": 4.0 + } + }, + "sockets": [ + { + "id": 2443477997896, + "type": "input", + "position": [ + 0.0, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + }, + { + "id": 2443477998184, + "type": "output", + "position": [ + 647.75, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + } + ] + }, + { + "id": 2443478874872, + "title": "Load MNIST Dataset", + "block_type": "code", + "source": "from tensorflow.keras.datasets import mnist\r\n(x_train, y_train), (x_test, y_test) = mnist.load_data()\r\n", + "position": [ + -710.2500000000002, + -384.25 + ], + "width": 787.0000000000001, + "height": 240.83333333333337, + "metadata": { + "title_metadata": { + "color": "white", + "font": "Ubuntu", + "size": 10, + "padding": 4.0 + } + }, + "sockets": [ + { + "id": 2443478910728, + "type": "output", + "position": [ + 787.0000000000001, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + } + ] + }, + { + "id": 2443478982728, + "title": "Normalize Image Dataset", + "block_type": "code", + "source": "x_train = x_train.astype('float32') / 255.0\r\nx_test = x_test.astype('float32') / 255.0\r\n\r\nx_train = x_train.reshape(x_train.shape[0], 28, 28, 1)\r\nx_test = x_test.reshape(x_test.shape[0], 28, 28, 1)\r\n\r\nprint('train:', x_train.shape, '|test:', x_test.shape)", + "position": [ + 256.50000000000045, + -538.7499999999998 + ], + "width": 733.2499999999995, + "height": 309.0, + "metadata": { + "title_metadata": { + "color": "white", + "font": "Ubuntu", + "size": 10, + "padding": 4.0 + } + }, + "sockets": [ + { + "id": 2443478983592, + "type": "input", + "position": [ + 0.0, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + }, + { + "id": 2443478983880, + "type": "output", + "position": [ + 733.2499999999995, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + } + ] + }, + { + "id": 2443479017656, + "title": "Build Keras CNN", + "block_type": "code", + "source": "import tensorflow as tf\r\nfrom tensorflow.keras.layers import (Dense, Flatten,\r\nConv2D, MaxPooling2D, Dropout)\r\nfrom tensorflow.keras.models import Sequential\r\n\r\nmodel = Sequential()\r\nmodel.add(Conv2D(28, kernel_size=(3,3), input_shape=x_train.shape[1:]))\r\nmodel.add(MaxPooling2D(pool_size=(2, 2)))\r\nmodel.add(Flatten())\r\nmodel.add(Dense(128, activation=tf.nn.relu))\r\nmodel.add(Dropout(0.2))\r\nmodel.add(Dense(10,activation=tf.nn.softmax))\r\n\r\nmodel.compile(optimizer='adam', \r\n loss='sparse_categorical_crossentropy', \r\n metrics=['accuracy'])\r\n", + "position": [ + 1039.5, + -350.7499999999999 + ], + "width": 863.0, + "height": 509.75, + "metadata": { + "title_metadata": { + "color": "white", + "font": "Ubuntu", + "size": 10, + "padding": 4.0 + } + }, + "sockets": [ + { + "id": 2443479018520, + "type": "input", + "position": [ + 0.0, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + }, + { + "id": 2443479018808, + "type": "output", + "position": [ + 863.0, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + } + ] + }, + { + "id": 2828158533848, + "title": "Plot Image Dataset Example", + "block_type": "code", + "source": "import matplotlib.pyplot as plt\r\nimport numpy as np\r\n\r\n# Display an example from the dataset\r\nrd_index = np.random.randint(len(x_train))\r\nplt.imshow(x_train[rd_index], cmap='gray')\r\nplt.title('Class '+ str(y_train[0]))\r\n\r\n", + "position": [ + 328.75, + -174.25 + ], + "width": 574.0, + "height": 565.5, + "metadata": { + "title_metadata": { + "color": "white", + "font": "Ubuntu", + "size": 10, + "padding": 4.0 + } + }, + "sockets": [ + { + "id": 2828158535432, + "type": "input", + "position": [ + 0.0, + 42.0 + ], + "metadata": { + "color": "#FF55FFF0", + "linecolor": "#FF000000", + "linewidth": 1.0, + "radius": 6.0 + } + } + ] + } + ], + "edges": [ + { + "id": 1643571233840, + "path_type": "bezier", + "source": { + "block": 2443479017656, + "socket": 2443479018808 + }, + "destination": { + "block": 2443477874008, + "socket": 2443477875016 + } + }, + { + "id": 2006783605056, + "path_type": "bezier", + "source": { + "block": 2443478874872, + "socket": 2443478910728 + }, + "destination": { + "block": 2828158533848, + "socket": 2828158535432 + } + }, + { + "id": 2006783606064, + "path_type": "bezier", + "source": { + "block": 2443477874008, + "socket": 2443477875160 + }, + "destination": { + "block": 2443477924600, + "socket": 2443477925608 + } + }, + { + "id": 2111730223424, + "path_type": "bezier", + "source": { + "block": 2443478982728, + "socket": 2443478983880 + }, + "destination": { + "block": 2443479017656, + "socket": 2443479018520 + } + }, + { + "id": 2111730224144, + "path_type": "bezier", + "source": { + "block": 2443477874008, + "socket": 2443477875160 + }, + "destination": { + "block": 2443477997032, + "socket": 2443477997896 + } + }, + { + "id": 2111730844864, + "path_type": "bezier", + "source": { + "block": 2443478874872, + "socket": 2443478910728 + }, + "destination": { + "block": 2443478982728, + "socket": 2443478983592 + } + } + ] +} \ No newline at end of file diff --git a/opencodeblocks/core/serializable.py b/opencodeblocks/core/serializable.py index ce1fb827..52c8675f 100644 --- a/opencodeblocks/core/serializable.py +++ b/opencodeblocks/core/serializable.py @@ -17,7 +17,7 @@ def serialize(self) -> OrderedDict: """ Serialize the object as an ordered dictionary. """ raise NotImplementedError() - def deserialize(self, data:OrderedDict, hashmap:dict=None, restore_id=True) -> None: + def deserialize(self, data: OrderedDict, hashmap: dict = None, restore_id=True) -> None: """ Deserialize the object from an ordered dictionary. Args: diff --git a/opencodeblocks/graphics/blocks/codeblock.py b/opencodeblocks/graphics/blocks/codeblock.py index bcadca5b..9c86bed4 100644 --- a/opencodeblocks/graphics/blocks/codeblock.py +++ b/opencodeblocks/graphics/blocks/codeblock.py @@ -16,6 +16,7 @@ ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + class OCBCodeBlock(OCBBlock): """ @@ -43,7 +44,7 @@ def __init__(self, **kwargs): self.resizing_source_code = False - self.update_all() # Set the geometry of display and source_editor + self.update_all() # Set the geometry of display and source_editor def init_source_editor(self): """ Initialize the python source code editor. """ @@ -56,11 +57,12 @@ def init_source_editor(self): @property def _editor_widget_height(self): return self.height - self.title_height - 2*self.edge_size \ - - self.output_panel_height + - self.output_panel_height @_editor_widget_height.setter def _editor_widget_height(self, value: int): - self.output_panel_height = self.height - value - self.title_height - 2*self.edge_size + self.output_panel_height = self.height - \ + value - self.title_height - 2*self.edge_size def update_all(self): """ Update the code block parts. """ @@ -94,7 +96,7 @@ def source(self) -> str: return self._source @source.setter - def source(self, value:str): + def source(self, value: str): self._source = value if hasattr(self, 'source_editor'): editor_widget = self.source_editor.widget() @@ -104,8 +106,9 @@ def source(self, value:str): def stdout(self) -> str: """ Code output. Be careful, this also includes stderr """ return self._stdout + @stdout.setter - def stdout(self, value:str): + def stdout(self, value: str): self._stdout = value if hasattr(self, 'source_editor'): # If there is a text output, erase the image output and display the text output @@ -123,7 +126,7 @@ def image(self) -> str: return self._image @image.setter - def image(self, value:str): + def image(self, value: str): self._image = value if hasattr(self, 'source_editor') and self.image != "": # If there is an image output, erase the text output and display the image output @@ -136,26 +139,26 @@ def image(self, value:str): qlabel.setPixmap(pixmap) @source.setter - def source(self, value:str): + def source(self, value: str): self._source = value if hasattr(self, 'source_editor'): editor_widget = self.source_editor.widget() editor_widget.setText(self._source) def paint(self, painter: QPainter, - option: QStyleOptionGraphicsItem, #pylint:disable=unused-argument - widget: Optional[QWidget]=None): #pylint:disable=unused-argument + option: QStyleOptionGraphicsItem, # pylint:disable=unused-argument + widget: Optional[QWidget] = None): # pylint:disable=unused-argument """ Paint the code output panel """ super().paint(painter, option, widget) path_title = QPainterPath() path_title.setFillRule(Qt.FillRule.WindingFill) path_title.addRoundedRect(0, 0, self.width, self.height, - self.edge_size, self.edge_size) + self.edge_size, self.edge_size) painter.setPen(Qt.PenStyle.NoPen) painter.setBrush(self._brush_background) painter.drawPath(path_title.simplified()) - def _is_in_resize_source_code_area(self, pos:QPointF): + def _is_in_resize_source_code_area(self, pos: QPointF): """ Return True if the given position is in the area used to resize the source code widget @@ -163,17 +166,17 @@ def _is_in_resize_source_code_area(self, pos:QPointF): source_editor_start = self.height - self.output_panel_height - self.edge_size return self.width - self.edge_size/2 < pos.x() and \ - source_editor_start - self.edge_size < pos.y() < source_editor_start + self.edge_size + source_editor_start - self.edge_size < pos.y() < source_editor_start + \ + self.edge_size - - def _is_in_resize_area(self, pos:QPointF): + def _is_in_resize_area(self, pos: QPointF): """ Return True if the given position is in the block resize_area. """ # This block features 2 resizing areas with 2 different behaviors is_in_bottom_left = super()._is_in_resize_area(pos) return is_in_bottom_left or self._is_in_resize_source_code_area(pos) - def _start_resize(self,pos:QPointF): + def _start_resize(self, pos: QPointF): self.resizing = True self.resize_start = pos if self._is_in_resize_source_code_area(pos): @@ -185,7 +188,7 @@ def _stop_resize(self): self.resizing_source_code = False QApplication.restoreOverrideCursor() - def mouseMoveEvent(self, event:QGraphicsSceneMouseEvent): + 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. @@ -195,12 +198,12 @@ def mouseMoveEvent(self, event:QGraphicsSceneMouseEvent): self.width = max(self.width + delta.x(), self._min_width) height_delta = max(delta.y(), - # List of all the quantities that must remain negative. - # Mainly: min_height - height must be negative for all elements - self._min_output_panel_height - self.output_panel_height, - self._min_height - self.height, - self._min_source_editor_height - self._editor_widget_height - ) + # List of all the quantities that must remain negative. + # Mainly: min_height - height must be negative for all elements + self._min_output_panel_height - self.output_panel_height, + self._min_height - self.height, + self._min_source_editor_height - self._editor_widget_height + ) self.height += height_delta if not self.resizing_source_code: diff --git a/opencodeblocks/graphics/function_parsing.py b/opencodeblocks/graphics/function_parsing.py index 1b371ec6..31842d8d 100644 --- a/opencodeblocks/graphics/function_parsing.py +++ b/opencodeblocks/graphics/function_parsing.py @@ -37,6 +37,7 @@ def run_with_variable_output(cell: str) -> None: output, done = kernel.update_output() print(output) + def get_function_name(code: str) -> str: """ Parses a string of code and returns the first function name it finds diff --git a/opencodeblocks/graphics/pyeditor.py b/opencodeblocks/graphics/pyeditor.py index 1770852d..c146561f 100644 --- a/opencodeblocks/graphics/pyeditor.py +++ b/opencodeblocks/graphics/pyeditor.py @@ -18,10 +18,11 @@ if TYPE_CHECKING: from opencodeblocks.graphics.view import OCBView + class PythonEditor(QsciScintilla): """ In-block python editor for OpenCodeBlocks. """ - + def __init__(self, block: OCBBlock): """ In-block python editor for OpenCodeBlocks. @@ -88,7 +89,7 @@ def views(self) -> List['OCBView']: """ Get the views in which the python_editor is present. """ return self.graphicsProxyWidget().scene().views() - def set_views_mode(self, mode:str): + def set_views_mode(self, mode: str): """ Set the views in which the python_editor is present to editing mode. """ for view in self.views(): if mode == "MODE_EDITING" or view.is_mode("MODE_EDITING"): diff --git a/opencodeblocks/graphics/qss/__init__.py b/opencodeblocks/graphics/qss/__init__.py index 5a5ecf9d..a44a510c 100644 --- a/opencodeblocks/graphics/qss/__init__.py +++ b/opencodeblocks/graphics/qss/__init__.py @@ -10,7 +10,8 @@ from opencodeblocks.graphics.qss import dark_resources -def loadStylesheets(filenames:List[str]): + +def loadStylesheets(filenames: List[str]): styles = '' for filename in filenames: file = QFile(filename) diff --git a/opencodeblocks/graphics/qss/dark_resources.py b/opencodeblocks/graphics/qss/dark_resources.py index 3b7d2693..a41ea54a 100644 --- a/opencodeblocks/graphics/qss/dark_resources.py +++ b/opencodeblocks/graphics/qss/dark_resources.py @@ -500,12 +500,15 @@ rcc_version = 2 qt_resource_struct = qt_resource_struct_v2 + def qInitResources(): QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, - qt_resource_name, qt_resource_data) + qt_resource_name, qt_resource_data) + def qCleanupResources(): QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, - qt_resource_name, qt_resource_data) + qt_resource_name, qt_resource_data) + qInitResources() diff --git a/opencodeblocks/graphics/theme.py b/opencodeblocks/graphics/theme.py index 0633bafc..57171f3f 100644 --- a/opencodeblocks/graphics/theme.py +++ b/opencodeblocks/graphics/theme.py @@ -7,6 +7,7 @@ from PyQt5.Qsci import QsciLexerPython from PyQt5.QtGui import QColor + class Theme: """ Class holding the details of a specific theme""" diff --git a/opencodeblocks/graphics/theme_manager.py b/opencodeblocks/graphics/theme_manager.py index f366f52d..9c088271 100644 --- a/opencodeblocks/graphics/theme_manager.py +++ b/opencodeblocks/graphics/theme_manager.py @@ -2,7 +2,8 @@ This module provides `theme_manager()`, a method that returns a handle to the theme manager of the application. -The theme manager provides the color scheme for the syntax highlighting of the text areas containing code. +The theme manager provides the color scheme for the syntax highlighting +of the text areas containing code. """ import os from typing import List @@ -12,12 +13,13 @@ from opencodeblocks.graphics.theme import Theme + class ThemeManager(QObject): """ Class loading theme files and providing the options set in those files """ themeChanged = pyqtSignal() - def __init__(self, parent = None): + def __init__(self, parent=None): """ Load the default themes and the fonts available to construct the ThemeManager """ super().__init__(parent) self._preferred_fonts = ["Inconsolata", "Roboto Mono", "Courier"] @@ -28,7 +30,7 @@ def __init__(self, parent = None): if font in available_fonts: self.recommended_font_family = font break - + self._themes = [] self._selected_theme_index = 0 theme_path = "./themes" @@ -40,6 +42,7 @@ def __init__(self, parent = None): with open(full_path, 'r', encoding="utf-8") as f: theme = Theme(name, f.read()) self._themes.append(theme) + @property def selected_theme_index(self): return self._selected_theme_index diff --git a/opencodeblocks/graphics/widget.py b/opencodeblocks/graphics/widget.py index 560c26bd..761b5ebf 100644 --- a/opencodeblocks/graphics/widget.py +++ b/opencodeblocks/graphics/widget.py @@ -11,6 +11,7 @@ from opencodeblocks.graphics.scene import OCBScene from opencodeblocks.graphics.view import OCBView + class OCBWidget(QWidget): """ Window for the OCB application. """ @@ -51,14 +52,15 @@ def isModified(self) -> bool: def savepath(self): """ Current cached file save path. Update window title when set.""" return self._savepath + @savepath.setter - def savepath(self, value:str): + def savepath(self, value: str): self._savepath = value self.updateTitle() def save(self): self.scene.save(self.savepath) - def load(self, filepath:str): + def load(self, filepath: str): self.scene.load(filepath) self.savepath = filepath diff --git a/opencodeblocks/graphics/window.py b/opencodeblocks/graphics/window.py index c33056e5..b355956a 100644 --- a/opencodeblocks/graphics/window.py +++ b/opencodeblocks/graphics/window.py @@ -25,15 +25,18 @@ class OCBWindow(QMainWindow): def __init__(self): super().__init__() - self.stylesheet_filename = os.path.join(os.path.dirname(__file__), 'qss', 'ocb.qss') + self.stylesheet_filename = os.path.join( + os.path.dirname(__file__), 'qss', 'ocb.qss') loadStylesheets(( os.path.join(os.path.dirname(__file__), 'qss', 'ocb_dark.qss'), self.stylesheet_filename )) self.mdiArea = QMdiArea() - self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) - self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.mdiArea.setHorizontalScrollBarPolicy( + Qt.ScrollBarPolicy.ScrollBarAsNeeded) + self.mdiArea.setVerticalScrollBarPolicy( + Qt.ScrollBarPolicy.ScrollBarAsNeeded) self.mdiArea.setViewMode(QMdiArea.ViewMode.TabbedView) self.mdiArea.setDocumentMode(True) self.mdiArea.setTabsMovable(True) @@ -92,50 +95,50 @@ def createActions(self): """ Create all menu actions. """ # File self.actNew = QAction('&New', statusTip='Create new ipygraph', - shortcut='Ctrl+N', triggered=self.onFileNew) + shortcut='Ctrl+N', triggered=self.onFileNew) self.actOpen = QAction('&Open', statusTip='Open an ipygraph', - shortcut='Ctrl+O', triggered=self.onFileOpen) + shortcut='Ctrl+O', triggered=self.onFileOpen) self.actSave = QAction('&Save', statusTip='Save the ipygraph', - shortcut='Ctrl+S', triggered=self.onFileSave) + shortcut='Ctrl+S', triggered=self.onFileSave) self.actSaveAs = QAction('Save &As...', statusTip='Save the ipygraph as...', - shortcut='Ctrl+Shift+S', triggered=self.onFileSaveAs) + shortcut='Ctrl+Shift+S', triggered=self.onFileSaveAs) self.actQuit = QAction('&Quit', statusTip='Save and Quit the application', - shortcut='Ctrl+Q', triggered=self.close) + shortcut='Ctrl+Q', triggered=self.close) # Edit self.actUndo = QAction('&Undo', statusTip='Undo last operation', - shortcut='Ctrl+Z', triggered=self.onEditUndo) + shortcut='Ctrl+Z', triggered=self.onEditUndo) self.actRedo = QAction('&Redo', statusTip='Redo last operation', - shortcut='Ctrl+Y', triggered=self.onEditRedo) + shortcut='Ctrl+Y', triggered=self.onEditRedo) self.actCut = QAction('Cu&t', statusTip='Cut to clipboard', - shortcut='Ctrl+X', triggered=self.onEditCut) + shortcut='Ctrl+X', triggered=self.onEditCut) self.actCopy = QAction('&Copy', statusTip='Copy to clipboard', - shortcut='Ctrl+C', triggered=self.onEditCopy) + shortcut='Ctrl+C', triggered=self.onEditCopy) self.actPaste = QAction('&Paste', statusTip='Paste from clipboard', - shortcut='Ctrl+V', triggered=self.onEditPaste) + shortcut='Ctrl+V', triggered=self.onEditPaste) self.actDel = QAction('&Del', statusTip='Delete selected items', - shortcut='Del', triggered=self.onEditDelete) + shortcut='Del', triggered=self.onEditDelete) # Window self.actClose = QAction("Cl&ose", self, - statusTip="Close the active window", - triggered=self.mdiArea.closeActiveSubWindow) + statusTip="Close the active window", + triggered=self.mdiArea.closeActiveSubWindow) self.actCloseAll = QAction("Close &All", self, - statusTip="Close all the windows", - triggered=self.mdiArea.closeAllSubWindows) + statusTip="Close all the windows", + triggered=self.mdiArea.closeAllSubWindows) self.actTile = QAction("&Tile", self, statusTip="Tile the windows", - triggered=self.mdiArea.tileSubWindows) + triggered=self.mdiArea.tileSubWindows) self.actCascade = QAction("&Cascade", self, - statusTip="Cascade the windows", - triggered=self.mdiArea.cascadeSubWindows) + statusTip="Cascade the windows", + triggered=self.mdiArea.cascadeSubWindows) self.actNext = QAction("Ne&xt", self, - shortcut=QKeySequence.StandardKey.NextChild, - statusTip="Move the focus to the next window", - triggered=self.mdiArea.activateNextSubWindow) + shortcut=QKeySequence.StandardKey.NextChild, + statusTip="Move the focus to the next window", + triggered=self.mdiArea.activateNextSubWindow) self.actPrevious = QAction("Pre&vious", self, - shortcut=QKeySequence.StandardKey.PreviousChild, - statusTip="Move the focus to the previous window", - triggered=self.mdiArea.activatePreviousSubWindow) + shortcut=QKeySequence.StandardKey.PreviousChild, + statusTip="Move the focus to the previous window", + triggered=self.mdiArea.activatePreviousSubWindow) self.actSeparator = QAction(self) self.actSeparator.setSeparator(True) @@ -208,7 +211,7 @@ def updateWindowMenu(self): action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) - def createNewMdiChild(self, filename:str=None): + def createNewMdiChild(self, filename: str = None): """ Create a new graph subwindow loading a file if a path is given. """ ocb_widget = OCBWidget() if filename is not None: @@ -223,7 +226,8 @@ def onFileNew(self): def onFileOpen(self): """ Open a file. """ - filename, _ = QFileDialog.getOpenFileName(self, 'Open ipygraph from file') + filename, _ = QFileDialog.getOpenFileName( + self, 'Open ipygraph from file') if filename == '': return if os.path.isfile(filename): @@ -256,7 +260,8 @@ def onFileSaveAs(self) -> bool: """ current_window = self.activeMdiChild() if current_window is not None: - filename, _ = QFileDialog.getSaveFileName(self, 'Save ipygraph to file') + filename, _ = QFileDialog.getSaveFileName( + self, 'Save ipygraph to file') if filename == '': return False current_window.savepath = filename @@ -307,7 +312,7 @@ def onEditDelete(self): # else: # event.ignore() - def closeEvent(self, event:QCloseEvent): + def closeEvent(self, event: QCloseEvent): """ Save and quit the application. """ self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): @@ -327,11 +332,12 @@ def maybeSave(self) -> bool: return True answer = QMessageBox.warning(self, "About to loose you work?", - "The file has been modified.\n Do you want to save your changes?", - QMessageBox.StandardButton.Save | - QMessageBox.StandardButton.Discard | - QMessageBox.StandardButton.Cancel - ) + "The file has been modified.\n" + "Do you want to save your changes?", + QMessageBox.StandardButton.Save | + QMessageBox.StandardButton.Discard | + QMessageBox.StandardButton.Cancel + ) if answer == QMessageBox.StandardButton.Save: return self.onFileSave() @@ -364,5 +370,6 @@ def writeSettings(self): def setActiveSubWindow(self, window): if window: self.mdiArea.setActiveSubWindow(window) + def setTheme(self, theme_index): - theme_manager().selected_theme_index = theme_index \ No newline at end of file + theme_manager().selected_theme_index = theme_index diff --git a/pylint_score.py b/pylint_score.py index 96d03cd7..896b9a97 100644 --- a/pylint_score.py +++ b/pylint_score.py @@ -10,6 +10,7 @@ from pylint.lint import Run from utils import score_to_rgb_color + class MyReporterClass(BaseReporter): """Report messages and layouts.""" @@ -51,6 +52,7 @@ def register(linter): """Register the reporter classes with the linter.""" linter.register_reporter(MyReporterClass) + if __name__ == '__main__': options = [ 'opencodeblocks', @@ -63,7 +65,8 @@ def register(linter): if sys.argv[1] == '--score': print(f"{score:.2f}") if score < score_min or score > score_max: - raise Exception(f'Insufficient score with pylint: {score:.2f}<{score_min:.2f}') + raise Exception( + f'Insufficient score with pylint: {score:.2f}<{score_min:.2f}') elif sys.argv[1] == '--color': print(score_to_rgb_color(score, score_min=score_min, score_max=score_max)) else: diff --git a/tests/integration/test_blocks.py b/tests/integration/test_blocks.py index 8ed63cee..0cff9521 100644 --- a/tests/integration/test_blocks.py +++ b/tests/integration/test_blocks.py @@ -6,12 +6,13 @@ """ # Imports needed for testing -import threading, queue +import threading +import queue import pytest from pytest_mock import MockerFixture import pytest_check as check import pyautogui - + # Packages tested from opencodeblocks.graphics.blocks.codeblock import OCBCodeBlock from opencodeblocks.graphics.window import OCBWindow @@ -20,10 +21,11 @@ from qtpy.QtWidgets import QApplication from PyQt5.QtCore import QPointF + class TestBlocks: @pytest.fixture(autouse=True) - def setup(self, mocker:MockerFixture): + def setup(self, mocker: MockerFixture): """ Setup reused variables. """ self.window = OCBWindow() self.ocb_widget = OCBWidget() @@ -43,14 +45,14 @@ def test_move_blocks(self, qtbot): QApplication.processEvents() - expected_move_amount = [70,-30] + expected_move_amount = [70, -30] STOP_MSG = "stop" CHECK_MSG = "check" msgQueue = queue.Queue() def testing_drag(msgQueue): - pos_block = QPointF(self.block1.pos().x(),self.block1.pos().y()) + pos_block = QPointF(self.block1.pos().x(), self.block1.pos().y()) pos_block.setX(pos_block.x() + self.block1.title_height/2) pos_block.setY(pos_block.y() + self.block1.title_height/2) @@ -58,7 +60,7 @@ def testing_drag(msgQueue): pos_block = self.ocb_widget.view.mapFromScene(pos_block) pos_block = self.ocb_widget.view.mapToGlobal(pos_block) - pyautogui.moveTo(pos_block.x(),pos_block.y()) + pyautogui.moveTo(pos_block.x(), pos_block.y()) pyautogui.mouseDown(button="left") iterations = 5 @@ -70,7 +72,7 @@ def testing_drag(msgQueue): pyautogui.mouseUp(button="left") - move_amount = [self.block1.pos().x(),self.block1.pos().y()] + move_amount = [self.block1.pos().x(), self.block1.pos().y()] # rectify because the scene can be zoomed : move_amount[0] = move_amount[0] * self.ocb_widget.view.zoom move_amount[1] = move_amount[1] * self.ocb_widget.view.zoom @@ -84,7 +86,6 @@ def testing_drag(msgQueue): msgQueue.put([STOP_MSG]) - t = threading.Thread(target=testing_drag, args=(msgQueue,)) t.start() @@ -95,10 +96,11 @@ def testing_drag(msgQueue): if msg[0] == STOP_MSG: break elif msg[0] == CHECK_MSG: - check.equal(msg[1],msg[2],msg[3]) + check.equal(msg[1], msg[2], msg[3]) t.join() self.window.close() + """ def test_running_python(qtbot): # The blocks should run arbitrary python when unfocused diff --git a/tests/integration/test_window.py b/tests/integration/test_window.py index 17c29dc4..1ab0ecee 100644 --- a/tests/integration/test_window.py +++ b/tests/integration/test_window.py @@ -11,10 +11,11 @@ from pytest_mock import MockerFixture from opencodeblocks.graphics.window import OCBWindow + class TestWindow: @pytest.fixture(autouse=True) - def setup(self, mocker:MockerFixture): + def setup(self, mocker: MockerFixture): """ Setup reused variables. """ self.window = OCBWindow() diff --git a/utils.py b/utils.py index 40d249ad..48dfb332 100644 --- a/utils.py +++ b/utils.py @@ -5,9 +5,11 @@ from colorsys import hsv_to_rgb + def interpolate(weight, x, y): return x * weight + (1-weight) * y + def score_to_rgb_color(score, score_min, score_max): normalized_score = max(0, (score - score_min) / (score_max - score_min)) hsv_color = (interpolate(normalized_score, 0.33, 0), 1, 1) From 43257addf4b686ef631eae980ff43d125b054be2 Mon Sep 17 00:00:00 2001 From: AlexandreSajus Date: Thu, 18 Nov 2021 16:00:33 +0100 Subject: [PATCH 2/4] :sparkles: removed unnecessary function --- opencodeblocks/graphics/function_parsing.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/opencodeblocks/graphics/function_parsing.py b/opencodeblocks/graphics/function_parsing.py index 31842d8d..7c18b9ac 100644 --- a/opencodeblocks/graphics/function_parsing.py +++ b/opencodeblocks/graphics/function_parsing.py @@ -20,24 +20,6 @@ def run_cell(cell: str) -> str: return kernel.execute(cell) -def run_with_variable_output(cell: str) -> None: - """ - This is a proof of concept to show that it is possible - to collect a variable output from a kernel execution - - Here the kernel executes the code and prints the output repeatedly - For example: if cell="model.fit(...)", this would print the progress bar progressing - - Args: - cell: String containing Python code - """ - kernel.client.execute(cell) - done = False - while not done: - output, done = kernel.update_output() - print(output) - - def get_function_name(code: str) -> str: """ Parses a string of code and returns the first function name it finds From 8e63b0371f1203a8f9c8211e7c6ba2aee09073a3 Mon Sep 17 00:00:00 2001 From: AlexandreSajus Date: Thu, 18 Nov 2021 16:03:08 +0100 Subject: [PATCH 3/4] :sparkles: added docstring --- opencodeblocks/graphics/kernel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/opencodeblocks/graphics/kernel.py b/opencodeblocks/graphics/kernel.py index 9fd3abd7..bdf64cd7 100644 --- a/opencodeblocks/graphics/kernel.py +++ b/opencodeblocks/graphics/kernel.py @@ -8,6 +8,8 @@ class Kernel(): + """jupyter_client kernel used to execute code and return output""" + def __init__(self): self.kernel_manager, self.client = start_new_kernel() From 4470666d4db35bac2b923f935fd7879f94d20657 Mon Sep 17 00:00:00 2001 From: AlexandreSajus Date: Thu, 18 Nov 2021 16:20:22 +0100 Subject: [PATCH 4/4] :truck: Moves mnist graph in examples --- mnist.ipyg => examples/mnist.ipyg | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mnist.ipyg => examples/mnist.ipyg (100%) diff --git a/mnist.ipyg b/examples/mnist.ipyg similarity index 100% rename from mnist.ipyg rename to examples/mnist.ipyg