Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
27022fb
Add beginning of ipynb conversion
FabienRoger Dec 2, 2021
775a220
Clean the converter and add TODOs
FabienRoger Dec 2, 2021
cb35b72
Correct pylint
FabienRoger Dec 2, 2021
0fe2e14
Merge remote-tracking branch 'origin/feature/new_block_types' into Ad…
FabienRoger Dec 3, 2021
21de930
Add conversion for markdown blocks
FabienRoger Dec 3, 2021
71c75a3
Correct Pylint
FabienRoger Dec 3, 2021
576c7aa
Apply black style
FabienRoger Dec 3, 2021
28fbfae
Merge remote-tracking branch 'origin/master' into Add-ipynb-conversio…
FabienRoger Dec 3, 2021
2b0686f
Fix display and empty text bug
FabienRoger Dec 3, 2021
ecef7f3
Add a title when a small markdown is given
FabienRoger Dec 3, 2021
2097422
Add sockets to converter
FabienRoger Dec 3, 2021
6d5e24d
Change file name
FabienRoger Dec 6, 2021
3e18fd0
Add skeleton for save as ipynb
FabienRoger Dec 6, 2021
15d5c61
Rename functions and variables for concistency
FabienRoger Dec 6, 2021
486a123
Remove unused function
FabienRoger Dec 6, 2021
53ffa0b
Add default for scene, edge and socket
FabienRoger Dec 6, 2021
8c60975
Merge branch 'bugfix/handle_default_fields' into Add-ipynb-conversion…
FabienRoger Dec 6, 2021
7d57f3a
Remove unecessary default data
FabienRoger Dec 6, 2021
fbae715
Add default for block and code block
FabienRoger Dec 6, 2021
a36e0c9
Merge branch 'bugfix/handle_default_fields' into Add-ipynb-conversion…
FabienRoger Dec 6, 2021
cf8107c
Removed uncessary block default
FabienRoger Dec 6, 2021
2c679e8
Fix bad default
FabienRoger Dec 6, 2021
155e340
Merge branch 'bugfix/handle_default_fields' into Add-ipynb-conversion…
FabienRoger Dec 6, 2021
e6032f2
Remove unused import + Add not implemented
FabienRoger Dec 6, 2021
f223d27
Remove uncessary .keys()
FabienRoger Dec 6, 2021
e233ae3
Merge branch 'bugfix/handle_default_fields' into Add-ipynb-conversion…
FabienRoger Dec 6, 2021
faecdb5
Refactor default data code
FabienRoger Dec 7, 2021
2d5fd3f
Merge remote-tracking branch 'origin/master' into Add-ipynb-conversio…
FabienRoger Dec 7, 2021
fe5cece
Add a very basic ipyg to ipynb converter
FabienRoger Dec 8, 2021
e40b2ca
Fix typo in function name
FabienRoger Dec 9, 2021
5949862
Dynamically determine the size of the block
FabienRoger Dec 9, 2021
60fc99b
Merge remote-tracking branch 'origin/HEAD' into Add-ipynb-conversion-…
FabienRoger Dec 9, 2021
b54f8cb
Fix last newline bug
FabienRoger Dec 9, 2021
5af131e
Add some typing
FabienRoger Dec 9, 2021
9aef64a
Fix lint + Improve coherence
FabienRoger Dec 9, 2021
6e497f0
Merge branch 'master' into Add-ipynb-conversion-draft
vanyle Dec 12, 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
23 changes: 19 additions & 4 deletions opencodeblocks/blocks/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,27 @@ 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",
source: str = "",
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 @@ -286,8 +298,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
9 changes: 9 additions & 0 deletions opencodeblocks/blocks/codeblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class OCBCodeBlock(OCBBlock):

"""

DEFAULT_DATA = {
**OCBBlock.DEFAULT_DATA,
"source": "",
}
MANDATORY_FIELDS = OCBBlock.MANDATORY_FIELDS

def __init__(self, **kwargs):
"""
Create a new OCBCodeBlock.
Expand Down Expand Up @@ -318,6 +324,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
16 changes: 15 additions & 1 deletion opencodeblocks/core/serializable.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@

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

from typing import OrderedDict
from typing import OrderedDict, Set


class Serializable:

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

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


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

Expand All @@ -30,3 +34,13 @@ def deserialize(

"""
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