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

📝 Add docstrings to graphics classes #15

Merged
merged 12 commits into from
Nov 2, 2021
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Pytest badge](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-tests.yml/badge.svg?branch=master)](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-tests.yml) [![Pylint badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FMathisFederico%2F00ce73155619a4544884ca6d251954b3%2Fraw%2Fopencodeblocks_pylint_badge.json)](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-pylint.yml) [![Unit coverage badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FMathisFederico%2F00ce73155619a4544884ca6d251954b3%2Fraw%2Fopencodeblocks_unit_coverage_badge.json)](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-coverage.yml) [![Integration coverage badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2FMathisFederico%2F00ce73155619a4544884ca6d251954b3%2Fraw%2Fopencodeblocks_integration_coverage_badge.json)](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-coverage.yml)

Whenever you encounter a :bug: **bug** or have :tada: **feature request**,
Whenever you encounter a :beetle: **bug** or have :tada: **feature request**,
report this via [GitHub issues](https://github.com/MathisFederico/OpenCodeBlocks/issues).

We are happy to receive contributions in the form of **pull requests** via GitHub.
Expand Down Expand Up @@ -39,7 +39,7 @@ You should also start your commit message with one or two applicable emoji. This
Emoji | Description
-----------------|-------------
:tada: `:tada:` | When you add a cool new feature
:bug: `:bug:` | When you fixed a bug
:beetle: `:beetle:` | When you fixed a bug
:fire: `:fire:` | When you removed something
:truck: `:truck:`| When you moved/renamed something
:wrench: `:wrench:` | When you improved/refactored a small piece of code
Expand Down
14 changes: 13 additions & 1 deletion opencodeblocks/core/serializable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,23 @@

class Serializable():

def __init__(self) -> None:
""" Serializable base for serializable objects. """

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

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:
""" Deserialize the object from an ordered dictionary.

Args:
data: Dictionnary containing data do deserialize from.
hashmap: Dictionnary mapping a hash code into knowed objects.
restore_id: If True, the id will be restored using the given data.
If False, a new id will be generated.

"""
raise NotImplementedError()
7 changes: 7 additions & 0 deletions opencodeblocks/graphics/blocks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# OpenCodeBlock an open-source tool for modular visual programing in python
# Copyright (C) 2021 Mathïs FEDERICO <https://www.gnu.org/licenses/>

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

from opencodeblocks.graphics.blocks.block import OCBBlock
from opencodeblocks.graphics.blocks.codeblock import OCBCodeBlock

BLOCKS = {
'base': OCBBlock,
'code': OCBCodeBlock
}
60 changes: 53 additions & 7 deletions opencodeblocks/graphics/blocks/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,29 @@

class OCBBlock(QGraphicsItem, Serializable):

def __init__(self, title:str='New block', block_type:str='base', source:str='',
position:tuple=(0, 0), title_color:str='white', title_font:str="Ubuntu",
""" Base class for blocks in OpenCodeBlocks. """

def __init__(self, block_type:str='base', source:str='', position:tuple=(0, 0),
width:int=300, height:int=200, edge_size:float=10.0,
title:str='New block', title_color:str='white', title_font:str="Ubuntu",
title_size:int=10, title_padding=4.0, parent: Optional['QGraphicsItem']=None):
""" Base class for blocks in OpenCodeBlocks.

Args:
block_type: Block type.
source: Block source text.
position: Block position in the scene.
width: Block width.
height: Block height.
edge_size: Block edges size.
title: Block title.
title_color: Color of the block title.
title_font: Font of the block title.
title_size: Size of the block title.
title_padding: Padding of the block title.
parent: Parent of the block.

"""
QGraphicsItem.__init__(self, parent=parent)
Serializable.__init__(self)

Expand All @@ -34,9 +54,9 @@ def __init__(self, title:str='New block', block_type:str='base', source:str='',
self._min_width = 300
self._min_height = 100

self.width = 300
self.height = 200
self.edge_size = 10.0
self.width = width
self.height = height
self.edge_size = edge_size

self.title_height = 3 * title_size
self.title_graphics = QGraphicsTextItem(self)
Expand Down Expand Up @@ -64,13 +84,17 @@ def __init__(self, title:str='New block', block_type:str='base', source:str='',
}

def scene(self) -> 'OCBScene':
""" Get the current OCBScene containing the block. """
return super().scene()

def boundingRect(self) -> QRectF:
""" Get the the block bounding box. """
return QRectF(0, 0, self.width, self.height).normalized()

def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem,
widget: Optional[QWidget]=None) -> None:
def paint(self, painter: QPainter,
option: QStyleOptionGraphicsItem, #pylint:disable=unused-argument
widget: Optional[QWidget]=None): #pylint:disable=unused-argument
""" Paint the block. """
# title
path_title = QPainterPath()
path_title.setFillRule(Qt.FillRule.WindingFill)
Expand Down Expand Up @@ -105,10 +129,12 @@ def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem,
painter.drawPath(path_outline.simplified())

def _is_in_resize_area(self, pos:QPointF):
""" Return True if the given position is in the block resize_area. """
return self.width - pos.x() < 2 * self.edge_size \
and self.height - pos.y() < 2 * self.edge_size

def get_socket_pos(self, socket:OCBSocket) -> Tuple[float]:
""" Get a socket position to place them on the block sides. """
if socket.socket_type == 'input':
x = 0
sockets = self.sockets_in
Expand All @@ -125,17 +151,20 @@ def get_socket_pos(self, socket:OCBSocket) -> Tuple[float]:
return x, y

def update_sockets(self):
""" Update the sockets positions. """
for socket in self.sockets_in + self.sockets_out:
socket.setPos(*self.get_socket_pos(socket))

def add_socket(self, socket:OCBSocket):
""" Add a socket to the block. """
if socket.socket_type == 'input':
self.sockets_in.append(socket)
else:
self.sockets_out.append(socket)
self.update_sockets()

def remove_socket(self, socket:OCBSocket):
""" Remove a socket from the block. """
if socket.socket_type == 'input':
self.sockets_in.remove(socket)
else:
Expand All @@ -144,6 +173,7 @@ def remove_socket(self, socket:OCBSocket):
self.update_sockets()

def mousePressEvent(self, event:QGraphicsSceneMouseEvent):
""" OCBBlock reaction to a mousePressEvent. """
pos = event.pos()
if self._is_in_resize_area(pos) and event.buttons() == Qt.MouseButton.LeftButton:
self.resize_start = pos
Expand All @@ -152,6 +182,7 @@ def mousePressEvent(self, event:QGraphicsSceneMouseEvent):
super().mousePressEvent(event)

def mouseReleaseEvent(self, event:QGraphicsSceneMouseEvent):
""" OCBBlock reaction to a mouseReleaseEvent. """
if self.resizing:
self.scene().history.checkpoint("Resized block", set_modified=True)
self.resizing = False
Expand All @@ -162,6 +193,7 @@ def mouseReleaseEvent(self, event:QGraphicsSceneMouseEvent):
super().mouseReleaseEvent(event)

def mouseMoveEvent(self, event:QGraphicsSceneMouseEvent):
""" OCBBlock reaction to a mouseMoveEvent. """
if self.resizing:
delta = event.pos() - self.resize_start
self.width = max(self.width + delta.x(), self._min_width)
Expand All @@ -174,25 +206,37 @@ def mouseMoveEvent(self, event:QGraphicsSceneMouseEvent):
self.moved = True

def setTitleGraphics(self, color:str, font:str, size:int, padding:float):
""" Set the title graphics.

Args:
color: title color.
font: title font.
size: title size.
padding: title padding.

"""
self.title_graphics.setDefaultTextColor(QColor(color))
self.title_graphics.setFont(QFont(font, size))
self.title_graphics.setPos(padding, 0)
self.title_graphics.setTextWidth(self.width - 2 * self.edge_size)

def remove(self):
""" Remove the block from the scene containing it. """
scene = self.scene()
for socket in self.sockets_in + self.sockets_out:
self.remove_socket(socket)
if scene is not None:
scene.removeItem(self)

def update_all(self):
""" Update sockets and title. """
self.update_sockets()
if hasattr(self, 'title_graphics'):
self.title_graphics.setTextWidth(self.width - 2 * self.edge_size)

@property
def title(self):
""" Block title. """
return self._title
@title.setter
def title(self, value:str):
Expand All @@ -202,6 +246,7 @@ def title(self, value:str):

@property
def width(self):
""" Block width. """
return self._width
@width.setter
def width(self, value:float):
Expand All @@ -210,6 +255,7 @@ def width(self, value:float):

@property
def height(self):
""" Block height. """
return self._height
@height.setter
def height(self, value:float):
Expand Down
7 changes: 6 additions & 1 deletion opencodeblocks/graphics/blocks/codeblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@

class OCBCodeBlock(OCBBlock):

""" Code Block. """

def __init__(self, **kwargs):
super().__init__(block_type='code', **kwargs)
self.source_editor = self.init_source_editor()

def init_source_editor(self):
""" Initialize the python source code editor. """
source_editor_graphics = QGraphicsProxyWidget(self)
source_editor = PythonEditor(self)
source_editor.setGeometry(
Expand All @@ -28,6 +31,7 @@ def init_source_editor(self):
return source_editor_graphics

def update_all(self):
""" Update the code block parts. """
if hasattr(self, 'source_editor'):
editor_widget = self.source_editor.widget()
editor_widget.setGeometry(
Expand All @@ -39,7 +43,8 @@ def update_all(self):
super().update_all()

@property
def source(self):
def source(self) -> str:
""" Source code. """
return self._source
@source.setter
def source(self, value:str):
Expand Down
34 changes: 32 additions & 2 deletions opencodeblocks/graphics/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,28 @@


class OCBEdge(QGraphicsPathItem, Serializable):
def __init__(self, path_type='bezier', edge_color="#001000", edge_selected_color="#00ff00",
edge_width:float=4.0,

""" 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.
path_type: Type of path, one of ('direct', 'bezier').
edge_color: Color of the edge.
edge_selected_color: Color of the edge when it is selected.
source: Source point of the directed edge.
destination: Destination point of the directed edge.
source_socket: Source socket of the directed edge, overrides source.
destination_socket: Destination socket of the directed edge, overrides destination.

"""

Serializable.__init__(self)
QGraphicsPathItem.__init__(self, parent=None)
self._pen = QPen(QColor(edge_color))
Expand Down Expand Up @@ -51,36 +68,48 @@ def __init__(self, path_type='bezier', edge_color="#001000", edge_selected_color
self.updateSocketsPosition()

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 = 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')

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

def updateSocketsPosition(self):
""" Update source and destination based on the sockets the edge is snaped to. """
if self.source_socket is not None:
self.source = self.source_socket.scenePos()
if self.destination_socket is not None:
self._destination = self.destination_socket.scenePos()

def paint(self, painter:QPainter,
option: QStyleOptionGraphicsItem, widget: Optional[QWidget]=None):
""" 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())
super().paint(painter, option, widget)

def update_path(self):
""" Update the edge path depending on the path_type. """
self.updateSocketsPosition()
path = QPainterPath(self.source)
if self.path_type == 'direct':
Expand All @@ -96,6 +125,7 @@ def update_path(self):

@property
def destination(self):
""" Destination point of the directed edge. """
return self._destination
@destination.setter
def destination(self, value):
Expand Down
Loading