Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
826e5e2
:tada: Add support for new block type. Reworked the block type system…
vanyle Nov 30, 2021
b938162
:tada: Removed prints and made the slider look nice
vanyle Nov 30, 2021
2560b80
:umbrella: Changed the examples and tests to match the new format.
vanyle Nov 30, 2021
3dcd205
:fire: Remove debug print
vanyle Nov 30, 2021
bda1d2a
:twisted_rightwards_arrows: Merge branch 'master' into feature/new_bl…
vanyle Nov 30, 2021
682c5d2
:tada: Add back the slider block
vanyle Nov 30, 2021
86f132a
:tada: Add ability for slider to execute python code
vanyle Nov 30, 2021
2305e3c
:tada: :memo: Create a basic markdown block !
vanyle Dec 2, 2021
f3f5fc7
:memo: :sparkles: Whitespace fixes and docstrings
vanyle Dec 2, 2021
0174653
:tada: Full support for markdown is here ! Display all your favorite …
vanyle Dec 2, 2021
6234076
:sparkles: Add docstring to improve pylint
vanyle Dec 2, 2021
1909b8f
:beetle: Update requirements.txt for the webview
vanyle Dec 2, 2021
3e2f6de
:tada: Implement the drawing block. A block where you can draw things…
vanyle Dec 2, 2021
10e6db7
:beetle: Fix serialization issue of codeblock
vanyle Dec 2, 2021
b37a4af
:tada: Add serialization support for all the new block types.
vanyle Dec 2, 2021
5eca2aa
:sparkles: :memo: Add docstrings and better whitespaces
vanyle Dec 2, 2021
e4a74b1
Merge branch 'master' into feature/new_block_types
vanyle Dec 3, 2021
04d7912
:fire: Removed autopep8 as the linker. We now use black.
vanyle Dec 3, 2021
9800182
Merge remote-tracking branch 'origin/feature/new_block_types' into fe…
vanyle Dec 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ You should also start your commit message with one or two applicable emoji. This

This section was inspired by [This repository](https://github.com/schneegans/dynamic-badges-action).

## Creating a new block type

You can checkout [this commit](https://github.com/MathisFederico/OpenCodeBlocks/commits/2305e3c92d88d2fd32644e7eab4c4e40246675d3) which contains the minimal amount of code required to
create a new block type.

## Version Numbers

Version numbers will be assigned according to the [Semantic Versioning](https://semver.org/) scheme.
Expand Down
2 changes: 1 addition & 1 deletion blocks/cnn_model.ocbb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "CNN",
"block_type": "code",
"block_type": "OCBCodeBlock",
"source": "input_size = 28\r\nclasses = 5\r\nmodel = Sequential()\r\nmodel.add(layers.Conv2D(input_size, kernel_size=(3,3), input_shape=(input_size,input_size,1)))\r\nmodel.add(layers.MaxPooling2D(pool_size=(2, 2)))\r\nmodel.add(layers.Flatten())\r\nmodel.add(layers.Dense(128, activation=tf.nn.relu))\r\nmodel.add(layers.Dropout(0.2))\r\nmodel.add(layers.Dense(classes,activation=tf.nn.softmax))\r\n\r\nmodel.compile(optimizer='adam', \r\n loss='sparse_categorical_crossentropy')",
"stdout": "",
"image": "",
Expand Down
16 changes: 16 additions & 0 deletions blocks/drawing.ocbb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"title": "Drawing",
"block_type": "OCBDrawingBlock",
"image": "",
"splitter_pos": [80,50],
"width": 600,
"height": 400,
"metadata": {
"title_metadata": {
"color": "white",
"font": "Ubuntu",
"size": 10,
"padding": 4.0
}
}
}
2 changes: 1 addition & 1 deletion blocks/empty.ocbb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "Empty",
"block_type": "code",
"block_type": "OCBCodeBlock",
"source": "",
"stdout": "",
"image": "",
Expand Down
2 changes: 1 addition & 1 deletion blocks/import_ml.ocbb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "Imports for ML",
"block_type": "code",
"block_type": "OCBCodeBlock",
"source": "import tensorflow as tf\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.models import Sequential",
"stdout": "",
"image": "",
Expand Down
16 changes: 16 additions & 0 deletions blocks/markdown.ocbb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"title": "Markdown",
"block_type": "OCBMarkdownBlock",
"text": "",
"splitter_pos": [88,41],
"width": 618,
"height": 184,
"metadata": {
"title_metadata": {
"color": "white",
"font": "Ubuntu",
"size": 10,
"padding": 4.0
}
}
}
16 changes: 16 additions & 0 deletions blocks/slider.ocbb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"title": "Slider",
"block_type": "OCBSliderBlock",
"source": "",
"splitter_pos": [88,41],
"width": 618,
"height": 184,
"metadata": {
"title_metadata": {
"color": "white",
"font": "Ubuntu",
"size": 10,
"padding": 4.0
}
}
}
72 changes: 36 additions & 36 deletions examples/mnist.ipyg

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions opencodeblocks/blocks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@

""" Module for the OCB Blocks of different types. """

from opencodeblocks.blocks.sliderblock import OCBSliderBlock
from opencodeblocks.blocks.block import OCBBlock
from opencodeblocks.blocks.codeblock import OCBCodeBlock

BLOCKS = {
'base': OCBBlock,
'code': OCBCodeBlock
}
from opencodeblocks.blocks.markdownblock import OCBMarkdownBlock
from opencodeblocks.blocks.drawingblock import OCBDrawingBlock
6 changes: 2 additions & 4 deletions opencodeblocks/blocks/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from opencodeblocks.blocks.widgets import OCBSplitter, OCBSizeGrip, OCBTitle

if TYPE_CHECKING:
from opencodeblocks.graphics.scene.scene import OCBScene
from opencodeblocks.scene.scene import OCBScene

BACKGROUND_COLOR = QColor("#E3212121")

Expand Down Expand Up @@ -274,8 +274,6 @@ def serialize(self) -> OrderedDict:
("id", self.id),
("title", self.title),
("block_type", self.block_type),
("source", self.source),
("stdout", self.stdout),
("splitter_pos", self.splitter.sizes()),
("position", [self.pos().x(), self.pos().y()]),
("width", self.width),
Expand All @@ -295,7 +293,7 @@ def deserialize(self, data: dict, hashmap: dict = None, restore_id=True) -> None
""" Restore the block from serialized data """
if restore_id:
self.id = data["id"]
for dataname in ("title", "block_type", "source", "stdout", "width", "height"):
for dataname in ("title", "block_type", "width", "height"):
setattr(self, dataname, data[dataname])

self.setPos(QPointF(*data["position"]))
Expand Down
35 changes: 28 additions & 7 deletions opencodeblocks/blocks/codeblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

""" Module for the base OCB Code Block. """

from typing import OrderedDict
from PyQt5.QtWidgets import QPushButton, QTextEdit

from ansi2html import Ansi2HTMLConverter
Expand Down Expand Up @@ -33,7 +34,7 @@ def __init__(self, **kwargs):
"""
self.source_editor = PythonEditor(self)

super().__init__(block_type="code", **kwargs)
super().__init__(**kwargs)

self.output_panel_height = self.height / 3
self._min_output_panel_height = 20
Expand Down Expand Up @@ -66,7 +67,8 @@ 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_code)
return run_button

Expand All @@ -83,6 +85,7 @@ def run_code(self):
self.source_editor.threadpool.start(worker)

def update_title(self):
""" Change the geometry of the title widget """
self.title_widget.setGeometry(
int(self.edge_size) + self.run_button.width(),
int(self.edge_size / 2),
Expand All @@ -91,20 +94,21 @@ def update_title(self):
)

def update_output_panel(self):
""" Change the geometry of the output panel """
# Close output panel if no output
if self.stdout == "":
self.previous_splitter_size = self.splitter.sizes()
self.output_closed = True
self.splitter.setSizes([1, 0])

def update_all(self):
"""Update the code block parts."""
""" Update the code block parts """
super().update_all()
self.update_output_panel()

@property
def source(self) -> str:
"""Source code."""
""" Source code """
return self.source_editor.text()

@source.setter
Expand All @@ -113,6 +117,7 @@ def source(self, value: str):

@property
def stdout(self) -> str:
""" Access the content of the output panel of the block """
return self._stdout

@stdout.setter
Expand All @@ -136,6 +141,7 @@ def stdout(self, value: str):

@staticmethod
def str_to_html(text: str):
""" Format text so that it's properly displayed by the code block """
# Remove carriage returns and backspaces
text = text.replace("\x08", "")
text = text.replace("\r", "")
Expand All @@ -148,7 +154,7 @@ def str_to_html(text: str):
return text

def handle_stdout(self, value: str):
"""Handle the stdout signal"""
""" Handle the stdout signal """
# If there is a new line
# Save every line but the last one

Expand All @@ -162,8 +168,23 @@ def handle_stdout(self, value: str):

@staticmethod
def b64_to_html(image: str):
""" Transform a base64 encoded image into a html image"""
return f'<img src="data:image/png;base64,{image}">'

def handle_image(self, image: str):
"""Handle the image signal"""
self.stdout = "<img>" + image
""" Handle the image signal """
self.stdout = '<img>' + image

def serialize(self):
base_dict = super().serialize()
base_dict["source"] = self.source
base_dict["stdout"] = self.stdout

return base_dict
def deserialize(self, data: OrderedDict,
hashmap: dict = None, restore_id: bool = True):
""" Restore a codeblock from it's serialized state """
for dataname in ('source', 'stdout'):
if dataname in data:
setattr(self, dataname, data[dataname])
super().deserialize(data, hashmap, restore_id)
115 changes: 115 additions & 0 deletions opencodeblocks/blocks/drawingblock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# pylint:disable=unused-argument

from math import floor
import json
from typing import OrderedDict

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor, QMouseEvent, QPaintEvent, QPainter
from PyQt5.QtWidgets import QPushButton, QWidget
from opencodeblocks.blocks.block import OCBBlock


eps = 1


class DrawableWidget(QWidget):
""" A drawable widget is a canvas like widget on which you can doodle """

def __init__(self, parent: QWidget):
""" Create a new Drawable widget """
super().__init__(parent)
self.setAttribute(Qt.WA_PaintOnScreen)
self.pixel_width = 24
self.pixel_height = 24
self.color_buffer = []
self.mouse_down = False
for _ in range(self.pixel_width):
self.color_buffer.append([])
for _ in range(self.pixel_height):
# color hex encoded as AARRGGBB
self.color_buffer[-1].append(0xFFFFFFFF)

def clearDrawing(self):
""" Clear the drawing """
for i in range(self.pixel_width):
for j in range(self.pixel_height):
self.color_buffer[i][j] = 0xFFFFFFFF

def paintEvent(self, evt: QPaintEvent):
""" Draw the content of the widget """
painter = QPainter(self)

for i in range(self.pixel_width):
self.color_buffer.append([])
for j in range(self.pixel_height):
w = self.width() / self.pixel_width
h = self.height() / self.pixel_height
painter.fillRect(
w * i,
h * j,
w + eps,
h + eps,
QColor.fromRgb(
self.color_buffer[i][j]))

def mouseMoveEvent(self, evt: QMouseEvent):
""" Change the drawing when dragging the mouse around"""
if self.mouse_down:
x = floor(evt.x() / self.width() * self.pixel_width)
y = floor(evt.y() / self.height() * self.pixel_height)
if 0 <= x < self.pixel_width and 0 <= y < self.pixel_height:
self.color_buffer[x][y] = 0xFF000000
self.repaint()

def mousePressEvent(self, evt: QMouseEvent):
""" Signal that the drawing starts """
self.mouse_down = True

def mouseReleaseEvent(self, evt: QMouseEvent):
""" Signal that the drawing stops """
self.mouse_down = False


class OCBDrawingBlock(OCBBlock):
""" An OCBBlock on which you can draw, to test your CNNs for example"""

def __init__(self, **kwargs):
""" Create a new OCBBlock"""
super().__init__(**kwargs)

self.draw_area = DrawableWidget(self.root)

self.splitter.addWidget(self.draw_area) # QGraphicsView
self.run_button = QPushButton("Clear", self.root)
self.run_button.move(int(self.edge_size * 2),
int(self.title_widget.height() + self.edge_size * 2))
self.run_button.setFixedSize(
int(8 * self.edge_size), int(3 * self.edge_size))
self.run_button.clicked.connect(self.draw_area.clearDrawing)
self.holder.setWidget(self.root)

@property
def drawing(self):
""" A json-encoded representation of the drawing """
return json.dumps(self.draw_area.color_buffer)

@drawing.setter
def drawing(self, value: str):
self.draw_area.color_buffer = json.loads(value)

def serialize(self):
""" Return a serialized version of this widget """
base_dict = super().serialize()
base_dict["drawing"] = self.drawing

return base_dict

def deserialize(self, data: OrderedDict,
hashmap: dict = None, restore_id: bool = True):
""" Restore a markdown block from it's serialized state """
for dataname in ['drawing']:
if dataname in data:
setattr(self, dataname, data[dataname])

super().deserialize(data, hashmap, restore_id)
Loading