Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/handle default fields #96

Merged
merged 4 commits into from
Dec 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 24 additions & 1 deletion opencodeblocks/blocks/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@

BACKGROUND_COLOR = QColor("#E3212121")

DEFAULT_BLOCK_DATA = {
"title": "_",
"splitter_pos": [88, 41],
"width": 618,
"height": 184,
"metadata": {"title_metadata": {"color": "white", "font": "Ubuntu", "size": 10}},
"sockets": [],
}
NONE_OPTIONAL_FIELDS = {"block_type", "position"}
MathisFederico marked this conversation as resolved.
Show resolved Hide resolved


class OCBBlock(QGraphicsItem, Serializable):

Expand Down Expand Up @@ -291,8 +301,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 All @@ -319,3 +332,13 @@ def deserialize(self, data: dict, hashmap: dict = None, restore_id=True) -> None
hashmap.update({socket_data["id"]: socket})

self.update_all()

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

for key in DEFAULT_BLOCK_DATA:
if key not in data:
data[key] = DEFAULT_BLOCK_DATA[key]
Comment on lines +336 to +344
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using global constants lead to a triple duplicate of this same code, a good example of why constants should be use with care !

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel like this is a duplicate, am I missing something ?

18 changes: 18 additions & 0 deletions opencodeblocks/blocks/codeblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

conv = Ansi2HTMLConverter()

DEFAULT_CODE_BLOCK_DATA = {"source": "", "output": ""}
NONE_OPTIONAL_FIELDS = {}


class OCBCodeBlock(OCBBlock):

Expand Down Expand Up @@ -197,7 +200,22 @@ 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])
super().deserialize(data, hashmap, restore_id)

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

for key in DEFAULT_CODE_BLOCK_DATA:
if key not in data:
data[key] = DEFAULT_CODE_BLOCK_DATA[key]

super().complete_with_default(data)
147 changes: 99 additions & 48 deletions opencodeblocks/graphics/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,26 @@
from opencodeblocks.core.serializable import Serializable
from opencodeblocks.graphics.socket import OCBSocket

DEFAULT_EDGE_DATA = {"path_type": "bezier"}
NONE_OPTIONAL_FIELDS = {"source", "destination"}

class OCBEdge(QGraphicsPathItem, Serializable):

""" Base class for directed edges in OpenCodeBlocks. """
class OCBEdge(QGraphicsPathItem, Serializable):

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

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.

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,33 +178,72 @@ 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:
self.remove()

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

for key in DEFAULT_EDGE_DATA:
if key not in data:
data[key] = DEFAULT_EDGE_DATA[key]
27 changes: 25 additions & 2 deletions opencodeblocks/graphics/socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@
from opencodeblocks.graphics.edge import OCBEdge
from opencodeblocks.blocks.block import OCBBlock

DEFAULT_SOCKET_DATA = {
"type": "input",
"metadata": {
"color": "#FF55FFF0",
"linecolor": "#FF000000",
"linewidth": 1.0,
"radius": 6.0,
},
}
NONE_OPTIONAL_FIELDS = {"position"}


class OCBSocket(QGraphicsItem, Serializable):

Expand Down Expand Up @@ -133,13 +144,25 @@ def serialize(self) -> OrderedDict:
)

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

self.complete_with_default(data)

self.socket_type = data["type"]
self.setPos(QPointF(*data["position"]))

self.metadata = dict(data["metadata"])
self.radius = self.metadata["radius"]
self._pen.setColor(QColor(self.metadata["linecolor"]))
self._pen.setWidth(int(self.metadata["linewidth"]))
self._brush.setColor(QColor(self.metadata["color"]))

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

for key in DEFAULT_SOCKET_DATA:
if key not in data:
data[key] = DEFAULT_SOCKET_DATA[key]
2 changes: 1 addition & 1 deletion opencodeblocks/scene/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def deserialize(self, data: OrderedDict,
hashmap: dict = None, restore_id: bool = True):
self.clear()
hashmap = hashmap if hashmap is not None else {}
if restore_id:
if restore_id and 'id' in data:
self.id = data['id']

# Create blocks
Expand Down