Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/Add-ipynb-conversion-draft' into…
Browse files Browse the repository at this point in the history
… feature/nested_node
  • Loading branch information
vanyle committed Dec 12, 2021
2 parents 58df0ce + 9aef64a commit b1762ce
Show file tree
Hide file tree
Showing 13 changed files with 618 additions and 134 deletions.
23 changes: 19 additions & 4 deletions opencodeblocks/blocks/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,26 @@ class OCBBlock(QGraphicsItem, Serializable):

"""Base class for blocks in OpenCodeBlocks."""

DEFAULT_DATA = {
"title": "New block",
"splitter_pos": [0, 0],
"width": 300,
"height": 200,
"metadata": {
"title_metadata": {"color": "white", "font": "Ubuntu", "size": 10}
},
"sockets": [],
}
MANDATORY_FIELDS = {"block_type", "position"}

def __init__(
self,
block_type: str = "base",
position: tuple = (0, 0),
width: int = 300,
height: int = 200,
width: int = DEFAULT_DATA["width"],
height: int = DEFAULT_DATA["height"],
edge_size: float = 10.0,
title: Union[OCBTitle, str] = "New block",
title: Union[OCBTitle, str] = DEFAULT_DATA["title"],
parent: Optional["QGraphicsItem"] = None,
):
"""Base class for blocks in OpenCodeBlocks.
Expand Down Expand Up @@ -282,8 +294,11 @@ def serialize(self) -> OrderedDict:

def deserialize(self, data: dict, hashmap: dict = None, restore_id=True) -> None:
"""Restore the block from serialized data"""
if restore_id:
if restore_id and "id" in data:
self.id = data["id"]

self.complete_with_default(data)

for dataname in ("title", "block_type", "width", "height"):
setattr(self, dataname, data[dataname])

Expand Down
11 changes: 11 additions & 0 deletions opencodeblocks/blocks/codeblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from PyQt5.QtWidgets import QPushButton, QTextEdit

from ansi2html import Ansi2HTMLConverter
from opencodeblocks.blocks.block import OCBBlock

from opencodeblocks.blocks.executableblock import OCBExecutableBlock
from opencodeblocks.graphics.socket import OCBSocket
Expand All @@ -28,10 +29,17 @@ class OCBCodeBlock(OCBExecutableBlock):
"""

def __init__(self, source: str = "", **kwargs):

"""
Create a new OCBCodeBlock.
Initialize all the child widgets specific to this block type
"""
DEFAULT_DATA = {
**OCBBlock.DEFAULT_DATA,
"source": "",
}
MANDATORY_FIELDS = OCBBlock.MANDATORY_FIELDS

super().__init__(**kwargs)
self.source_editor = PythonEditor(self)

Expand Down Expand Up @@ -233,6 +241,9 @@ def deserialize(
self, data: OrderedDict, hashmap: dict = None, restore_id: bool = True
):
"""Restore a codeblock from it's serialized state"""

self.complete_with_default(data)

for dataname in ("source", "stdout"):
if dataname in data:
setattr(self, dataname, data[dataname])
Expand Down
11 changes: 4 additions & 7 deletions opencodeblocks/blocks/containerblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

from PyQt5.QtWidgets import QVBoxLayout
from opencodeblocks.blocks.block import OCBBlock
from opencodeblocks.graphics.view import OCBView
from opencodeblocks.graphics.widget import OCBWidget
from opencodeblocks.scene.scene import OCBScene


class OCBContainerBlock(OCBBlock):
Expand All @@ -19,9 +16,9 @@ def __init__(self, **kwargs):

self.layout = QVBoxLayout(self.root)

self.scene = OCBScene()
self.scene.addHasBeenModifiedListener(self.updateTitle)
self.view = OCBView(self.scene)
self.layout.addWidget(self.view)
#self.scene = OCBScene()
#self.scene.addHasBeenModifiedListener(self.updateTitle)
#self.view = OCBView(self.scene)
#self.layout.addWidget(self.view)

self.holder.setWidget(self.root)
27 changes: 21 additions & 6 deletions opencodeblocks/core/serializable.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,27 @@

""" Module for the Serializable base class """

from typing import OrderedDict
from typing import OrderedDict, Set


class Serializable():
class Serializable:

""" Serializable base for serializable objects. """
"""Serializable base for serializable objects."""

MANDATORY_FIELDS: OrderedDict = {}
DEFAULT_DATA: Set[str] = {}

def __init__(self):
self.id = id(self)

def serialize(self) -> OrderedDict:
""" Serialize the object as an ordered dictionary. """
"""Serialize the object as an ordered dictionary."""
raise NotImplementedError()

def deserialize(self, data: OrderedDict, hashmap: dict = None, restore_id=True) -> None:
""" Deserialize the object from an ordered dictionary.
def deserialize(
self, data: OrderedDict, hashmap: dict = None, restore_id=True
) -> None:
"""Deserialize the object from an ordered dictionary.
Args:
data: Dictionnary containing data do deserialize from.
Expand All @@ -28,3 +33,13 @@ def deserialize(self, data: OrderedDict, hashmap: dict = None, restore_id=True)
"""
raise NotImplementedError()

def complete_with_default(self, data: OrderedDict) -> None:
"""Add default data in place when fields are missing"""
for key in self.MANDATORY_FIELDS:
if key not in data:
raise ValueError(f"{key} of the socket is missing")

for key in self.DEFAULT_DATA:
if key not in data:
data[key] = self.DEFAULT_DATA[key]
137 changes: 89 additions & 48 deletions opencodeblocks/graphics/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,23 @@

class OCBEdge(QGraphicsPathItem, Serializable):

""" Base class for directed edges in OpenCodeBlocks. """

def __init__(self, edge_width: float = 4.0, path_type='bezier',
edge_color="#001000", edge_selected_color="#00ff00",
source: QPointF = QPointF(0, 0), destination: QPointF = QPointF(0, 0),
source_socket: OCBSocket = None, destination_socket: OCBSocket = None
):
""" Base class for edges in OpenCodeBlocks.
"""Base class for directed edges in OpenCodeBlocks."""

DEFAULT_DATA = {"path_type": "bezier"}
MANDATORY_FIELDS = {"source", "destination"}

def __init__(
self,
edge_width: float = 4.0,
path_type = DEFAULT_DATA["path_type"],
edge_color="#001000",
edge_selected_color="#00ff00",
source: QPointF = QPointF(0, 0),
destination: QPointF = QPointF(0, 0),
source_socket: OCBSocket = None,
destination_socket: OCBSocket = None,
):
"""Base class for edges in OpenCodeBlocks.
Args:
edge_width: Width of the edge.
Expand Down Expand Up @@ -62,58 +71,61 @@ def __init__(self, edge_width: float = 4.0, path_type='bezier',
self._destination = destination
self.update_path()

def remove_from_socket(self, socket_type='source'):
""" Remove the edge from the sockets it is snaped to on the given socket_type.
def remove_from_socket(self, socket_type="source"):
"""Remove the edge from the sockets it is snaped to on the given socket_type.
Args:
socket_type: One of ('source', 'destination').
"""
socket_name = f'{socket_type}_socket'
socket_name = f"{socket_type}_socket"
socket = getattr(self, socket_name, OCBSocket)
if socket is not None:
socket.remove_edge(self)
setattr(self, socket_name, None)

def remove_from_sockets(self):
""" Remove the edge from all sockets it is snaped to. """
self.remove_from_socket('source')
self.remove_from_socket('destination')
"""Remove the edge from all sockets it is snaped to."""
self.remove_from_socket("source")
self.remove_from_socket("destination")

def remove(self):
""" Remove the edge from the scene in which it is drawn. """
"""Remove the edge from the scene in which it is drawn."""
scene = self.scene()
if scene is not None:
self.remove_from_sockets()
scene.removeItem(self)

def paint(self, painter: QPainter,
option: QStyleOptionGraphicsItem, # pylint:disable=unused-argument
widget: Optional[QWidget] = None): # pylint:disable=unused-argument
""" Paint the edge. """
def paint(
self,
painter: QPainter,
option: QStyleOptionGraphicsItem, # pylint:disable=unused-argument
widget: Optional[QWidget] = None,
): # pylint:disable=unused-argument
"""Paint the edge."""
self.update_path()
pen = self._pen_dragging if self.destination_socket is None else self._pen
painter.setPen(self._pen_selected if self.isSelected() else pen)
painter.setBrush(Qt.BrushStyle.NoBrush)
painter.drawPath(self.path())

def update_path(self):
""" Update the edge path depending on the path_type. """
"""Update the edge path depending on the path_type."""
path = QPainterPath(self.source)
if self.path_type == 'direct':
if self.path_type == "direct":
path.lineTo(self.destination)
elif self.path_type == 'bezier':
elif self.path_type == "bezier":
sx, sy = self.source.x(), self.source.y()
dx, dy = self.destination.x(), self.destination.y()
mid_dist = (dx - sx) / 2
path.cubicTo(sx + mid_dist, sy, dx - mid_dist, dy, dx, dy)
else:
raise NotImplementedError(f'Unknowed path type: {self.path_type}')
raise NotImplementedError(f"Unknowed path type: {self.path_type}")
self.setPath(path)

@property
def source(self) -> QPointF:
""" Source point of the directed edge. """
"""Source point of the directed edge."""
if self.source_socket is not None:
return self.source_socket.scenePos()
return self._source
Expand All @@ -128,7 +140,7 @@ def source(self, value: QPointF):

@property
def source_socket(self) -> OCBSocket:
""" Source socket of the directed edge. """
"""Source socket of the directed edge."""
return self._source_socket

@source_socket.setter
Expand All @@ -140,7 +152,7 @@ def source_socket(self, value: OCBSocket):

@property
def destination(self) -> QPointF:
""" Destination point of the directed edge. """
"""Destination point of the directed edge."""
if self.destination_socket is not None:
return self.destination_socket.scenePos()
return self._destination
Expand All @@ -155,7 +167,7 @@ def destination(self, value: QPointF):

@property
def destination_socket(self) -> OCBSocket:
""" Destination socket of the directed edge. """
"""Destination socket of the directed edge."""
return self._destination_socket

@destination_socket.setter
Expand All @@ -166,32 +178,61 @@ def destination_socket(self, value: OCBSocket):
self.destination = value.scenePos()

def serialize(self) -> OrderedDict:
return OrderedDict([
('id', self.id),
('path_type', self.path_type),
('source', OrderedDict([
('block',
self.source_socket.block.id if self.source_socket else None),
('socket',
self.source_socket.id if self.source_socket else None)
])),
('destination', OrderedDict([
('block',
self.destination_socket.block.id if self.destination_socket else None),
('socket',
self.destination_socket.id if self.destination_socket else None)
]))
])
return OrderedDict(
[
("id", self.id),
("path_type", self.path_type),
(
"source",
OrderedDict(
[
(
"block",
self.source_socket.block.id
if self.source_socket
else None,
),
(
"socket",
self.source_socket.id if self.source_socket else None,
),
]
),
),
(
"destination",
OrderedDict(
[
(
"block",
self.destination_socket.block.id
if self.destination_socket
else None,
),
(
"socket",
self.destination_socket.id
if self.destination_socket
else None,
),
]
),
),
]
)

def deserialize(self, data: OrderedDict, hashmap: dict = None, restore_id=True):
if restore_id:
self.id = data['id']
self.path_type = data['path_type']
if restore_id and "id" in data:
self.id = data["id"]

self.complete_with_default(data)

self.path_type = data["path_type"]
try:
self.source_socket = hashmap[data['source']['socket']]
self.source_socket = hashmap[data["source"]["socket"]]
self.source_socket.add_edge(self, is_destination=False)

self.destination_socket = hashmap[data['destination']['socket']]
self.destination_socket = hashmap[data["destination"]["socket"]]
self.destination_socket.add_edge(self, is_destination=True)
self.update_path()
except KeyError:
Expand Down
Loading

0 comments on commit b1762ce

Please sign in to comment.