diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 827d9249..f52c6233 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,7 +2,7 @@
[](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-tests.yml) [](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-pylint.yml) [](https://github.com/MathisFederico/OpenCodeBlocks/actions/workflows/python-coverage.yml) [](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.
@@ -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
diff --git a/opencodeblocks/core/serializable.py b/opencodeblocks/core/serializable.py
index 4d3a1fe6..ce1fb827 100644
--- a/opencodeblocks/core/serializable.py
+++ b/opencodeblocks/core/serializable.py
@@ -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()
diff --git a/opencodeblocks/graphics/blocks/__init__.py b/opencodeblocks/graphics/blocks/__init__.py
index 57ca1243..893604f8 100644
--- a/opencodeblocks/graphics/blocks/__init__.py
+++ b/opencodeblocks/graphics/blocks/__init__.py
@@ -1,5 +1,12 @@
# OpenCodeBlock an open-source tool for modular visual programing in python
# Copyright (C) 2021 Mathïs FEDERICO
+""" 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
+}
diff --git a/opencodeblocks/graphics/blocks/block.py b/opencodeblocks/graphics/blocks/block.py
index 8f66a6a5..90630bf6 100644
--- a/opencodeblocks/graphics/blocks/block.py
+++ b/opencodeblocks/graphics/blocks/block.py
@@ -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)
@@ -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)
@@ -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)
@@ -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
@@ -125,10 +151,12 @@ 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:
@@ -136,6 +164,7 @@ def add_socket(self, socket:OCBSocket):
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:
@@ -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
@@ -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
@@ -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)
@@ -174,12 +206,22 @@ 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)
@@ -187,12 +229,14 @@ def remove(self):
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):
@@ -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):
@@ -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):
diff --git a/opencodeblocks/graphics/blocks/codeblock.py b/opencodeblocks/graphics/blocks/codeblock.py
index 67219e14..306a2e96 100644
--- a/opencodeblocks/graphics/blocks/codeblock.py
+++ b/opencodeblocks/graphics/blocks/codeblock.py
@@ -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(
@@ -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(
@@ -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):
diff --git a/opencodeblocks/graphics/edge.py b/opencodeblocks/graphics/edge.py
index 8a3df5b8..0666bd6b 100644
--- a/opencodeblocks/graphics/edge.py
+++ b/opencodeblocks/graphics/edge.py
@@ -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))
@@ -51,6 +68,12 @@ 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:
@@ -58,15 +81,18 @@ def remove_from_socket(self, socket_type='source'):
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:
@@ -74,13 +100,16 @@ def updateSocketsPosition(self):
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':
@@ -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):
diff --git a/opencodeblocks/graphics/pyeditor.py b/opencodeblocks/graphics/pyeditor.py
index 9a2ab71f..88e6c1d3 100644
--- a/opencodeblocks/graphics/pyeditor.py
+++ b/opencodeblocks/graphics/pyeditor.py
@@ -3,17 +3,28 @@
""" Module for OCB in block python editor. """
+from typing import TYPE_CHECKING, List
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFocusEvent, QFont, QFontMetrics, QColor
from PyQt5.Qsci import QsciScintilla, QsciLexerPython
from opencodeblocks.graphics.blocks.block import OCBBlock
+if TYPE_CHECKING:
+ from opencodeblocks.graphics.view import OCBView
class PythonEditor(QsciScintilla):
- def __init__(self, block:OCBBlock, parent=None):
- super().__init__(parent)
+ """ In-block python editor for OpenCodeBlocks. """
+
+ def __init__(self, block:OCBBlock):
+ """ In-block python editor for OpenCodeBlocks.
+
+ Args:
+ block: Block in which to add the python editor widget.
+
+ """
+ super().__init__(None)
self.block = block
self.setText(self.block.source)
@@ -86,16 +97,23 @@ def __init__(self, block:OCBBlock, parent=None):
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
+ def views(self) -> List['OCBView']:
+ """ Get the views in which the python_editor is present. """
+ return self.graphicsProxyWidget().scene().views()
+
def set_views_mode(self, mode:str):
- for view in self.graphicsProxyWidget().scene().views():
+ """ Set the views in which the python_editor is present to editing mode. """
+ for view in self.views():
if mode == "MODE_EDITING" or view.is_mode("MODE_EDITING"):
view.set_mode(mode)
def focusInEvent(self, event: QFocusEvent):
+ """ PythonEditor reaction to PyQt focusIn events. """
self.set_views_mode("MODE_EDITING")
return super().focusInEvent(event)
def focusOutEvent(self, event: QFocusEvent):
+ """ PythonEditor reaction to PyQt focusOut events. """
self.set_views_mode("MODE_NOOP")
if self.isModified():
self.block.source = self.text()
diff --git a/opencodeblocks/graphics/scene/__init__.py b/opencodeblocks/graphics/scene/__init__.py
index 430e5673..efb3c80c 100644
--- a/opencodeblocks/graphics/scene/__init__.py
+++ b/opencodeblocks/graphics/scene/__init__.py
@@ -1,4 +1,6 @@
# OpenCodeBlock an open-source tool for modular visual programing in python
# Copyright (C) 2021 Mathïs FEDERICO
+""" Module for the OCBScene creation and manipulations. """
+
from opencodeblocks.graphics.scene.scene import OCBScene
diff --git a/opencodeblocks/graphics/scene/clipboard.py b/opencodeblocks/graphics/scene/clipboard.py
index 34f375c1..b377359e 100644
--- a/opencodeblocks/graphics/scene/clipboard.py
+++ b/opencodeblocks/graphics/scene/clipboard.py
@@ -18,26 +18,28 @@
class SceneClipboard():
- """ Helper object to handle clipboard operations on an OCBScene.
- Args:
- scene: Scene reference.
-
- """
+ """ Helper object to handle clipboard operations on an OCBScene. """
def __init__(self, scene:'OCBScene'):
- self.scene = scene
+ """ Helper object to handle clipboard operations on an OCBScene.
- def views(self) -> 'OCBView':
- return super().views()
+ Args:
+ scene: Scene reference.
+
+ """
+ self.scene = scene
def cut(self):
+ """ Cut the selected items and put them into clipboard. """
self._store(self._serializeSelected(delete=True))
def copy(self):
+ """ Copy the selected items into clipboard. """
self._store(self._serializeSelected(delete=False))
def paste(self):
+ """ Paste the items in clipboard into the current scene. """
self._deserializeData(self._gatherData())
def _serializeSelected(self, delete=False) -> OrderedDict:
diff --git a/opencodeblocks/graphics/scene/scene.py b/opencodeblocks/graphics/scene/scene.py
index 9e83137b..c7b6c68b 100644
--- a/opencodeblocks/graphics/scene/scene.py
+++ b/opencodeblocks/graphics/scene/scene.py
@@ -50,6 +50,7 @@ def __init__(self, parent=None,
@property
def has_been_modified(self):
+ """ True if the scene has been modified, False otherwise. """
return self._has_been_modified
@has_been_modified.setter
def has_been_modified(self, value:bool):
@@ -63,9 +64,11 @@ def has_been_modified(self, value:bool):
self._has_been_modified = value
def addHasBeenModifiedListener(self, callback:FunctionType):
+ """ Add a callback that will trigger when the scene has been modified. """
self._has_been_modified_listeners.append(callback)
def sortedSelectedItems(self) -> List[Union[OCBBlock, OCBEdge]]:
+ """ Returns the selected blocks and selected edges in two separate lists. """
selected_blocks, selected_edges = [], []
for item in self.selectedItems():
if isinstance(item, OCBBlock):
@@ -115,10 +118,12 @@ def drawGrid(self, painter: QPainter, rect: QRectF):
painter.drawLines(*lines_light)
def save(self, filepath:str):
+ """ Save the scene into filepath. """
self.save_to_ipyg(filepath)
self.has_been_modified = False
def save_to_ipyg(self, filepath:str):
+ """ Save the scene into filepath as interactive python graph (.ipyg). """
if '.' not in filepath:
filepath += '.ipyg'
@@ -130,6 +135,12 @@ def save_to_ipyg(self, filepath:str):
file.write(json.dumps(self.serialize(), indent=4))
def load(self, filepath:str):
+ """ Load a saved scene.
+
+ Args:
+ filepath: Path to the file to load.
+
+ """
if filepath.endswith('.ipyg'):
data = self.load_from_ipyg(filepath)
else:
@@ -140,11 +151,18 @@ def load(self, filepath:str):
self.has_been_modified = False
def load_from_ipyg(self, filepath:str):
+ """ Load an interactive python graph (.ipyg) into the scene.
+
+ Args:
+ filepath: Path to the .ipyg file to load.
+
+ """
with open(filepath, 'r', encoding='utf-8') as file:
data = json.loads(file.read())
return data
def clear(self):
+ """ Clear the scene from all items. """
self.has_been_modified = False
return super().clear()
diff --git a/opencodeblocks/graphics/socket.py b/opencodeblocks/graphics/socket.py
index 256f7fee..e7bc2dad 100644
--- a/opencodeblocks/graphics/socket.py
+++ b/opencodeblocks/graphics/socket.py
@@ -19,8 +19,21 @@
class OCBSocket(QGraphicsItem, Serializable):
+ """ Base class for sockets in OpenCodeBlocks. """
+
def __init__(self, block:'OCBBlock', socket_type:str='undefined', radius:float=6.0,
color:str='#FF55FFF0', linewidth:float=1.0, linecolor:str='#FF000000'):
+ """ Base class for sockets in OpenCodeBlocks.
+
+ Args:
+ block: Block containing the socket.
+ socket_type: Type of the socket.
+ radius: Radius of the socket graphics.
+ color: Color of the socket graphics.
+ linewidth: Linewidth of the socket graphics.
+ linecolor: Linecolor of the socket graphics.
+
+ """
Serializable.__init__(self)
self.block = block
QGraphicsItem.__init__(self, parent=self.block)
@@ -41,26 +54,32 @@ def __init__(self, block:'OCBBlock', socket_type:str='undefined', radius:float=6
}
def add_edge(self, edge:'OCBEdge'):
+ """ Add a given edge to the socket edges. """
self.edges.append(edge)
def remove_edge(self, edge:'OCBEdge'):
+ """ Remove a given edge from the socket edges. """
self.edges.remove(edge)
def remove(self):
+ """ Remove the socket and all its edges from the scene it is in. """
for edge in self.edges:
edge.remove()
scene = self.scene()
if scene is not None:
scene.removeItem(self)
- def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem,
- widget: Optional[QWidget]=None):
+ def paint(self, painter: QPainter,
+ option: QStyleOptionGraphicsItem, #pylint:disable=unused-argument
+ widget: Optional[QWidget]=None): #pylint:disable=unused-argument
+ """ Paint the socket. """
painter.setBrush(self._brush)
painter.setPen(self._pen)
r = self.radius
painter.drawEllipse(int(-r),int(-r),int(2*r),int(2*r))
def boundingRect(self) -> QRectF:
+ """ Get the socket bounding box. """
r = self.radius
return QRectF(-r, -r, 2*r, 2*r)
diff --git a/opencodeblocks/graphics/view.py b/opencodeblocks/graphics/view.py
index 880447f9..f5057eff 100644
--- a/opencodeblocks/graphics/view.py
+++ b/opencodeblocks/graphics/view.py
@@ -6,7 +6,7 @@
from PyQt5.QtCore import QEvent, QPointF, Qt
from PyQt5.QtGui import QMouseEvent, QPainter, QWheelEvent
from PyQt5.QtWidgets import QGraphicsView
-from sip import isdeleted
+from PyQt5.sip import isdeleted
from opencodeblocks.graphics.scene import OCBScene
from opencodeblocks.graphics.socket import OCBSocket
@@ -64,10 +64,11 @@ def init_ui(self):
self.setDragMode(QGraphicsView.DragMode.RubberBandDrag)
def scene(self) -> OCBScene:
+ """ Get current OCBScene. """
return super().scene()
def mousePressEvent(self, event: QMouseEvent):
- """Dispatch Qt's mousePress events to corresponding functions below"""
+ """ Dispatch Qt's mousePress events to corresponding functions below. """
if event.button() == Qt.MouseButton.MiddleButton:
self.middleMouseButtonPress(event)
elif event.button() == Qt.MouseButton.LeftButton:
@@ -77,12 +78,6 @@ def mousePressEvent(self, event: QMouseEvent):
else:
super().mousePressEvent(event)
- def mouseMoveEvent(self, event: QMouseEvent) -> None:
- self.lastMousePos = self.mapToScene(event.pos())
- self.drag_edge(event, 'move')
- if event is not None:
- super().mouseMoveEvent(event)
-
def mouseReleaseEvent(self, event: QMouseEvent):
"""Dispatch Qt's mouseRelease events to corresponding functions below"""
if event.button() == Qt.MouseButton.MiddleButton:
@@ -94,34 +89,58 @@ def mouseReleaseEvent(self, event: QMouseEvent):
else:
super().mouseReleaseEvent(event)
+ def mouseMoveEvent(self, event: QMouseEvent) -> None:
+ """ OCBView reaction to mouseMoveEvent. """
+ self.lastMousePos = self.mapToScene(event.pos())
+ self.drag_edge(event, 'move')
+ if event is not None:
+ super().mouseMoveEvent(event)
+
def middleMouseButtonPress(self, event: QMouseEvent):
+ """ OCBView reaction to middleMouseButtonPress event. """
super().mousePressEvent(event)
def middleMouseButtonRelease(self, event: QMouseEvent):
+ """ OCBView reaction to middleMouseButtonRelease event. """
super().mouseReleaseEvent(event)
def leftMouseButtonPress(self, event: QMouseEvent):
- event = self.bring_forward(event)
+ """ OCBView reaction to leftMouseButtonPress event. """
+ # If clicked on a block, bring it forward.
+ item_at_click = self.itemAt(event.pos())
+ if item_at_click is not None:
+ while item_at_click.parentItem() is not None:
+ if isinstance(item_at_click,OCBBlock):
+ break
+ item_at_click = item_at_click.parentItem()
+
+ if isinstance(item_at_click, OCBBlock):
+ self.bring_block_forward(item_at_click)
+
+ # If clicked on a socket, start dragging an edge.
event = self.drag_edge(event, 'press')
if event is not None:
super().mousePressEvent(event)
def leftMouseButtonRelease(self, event: QMouseEvent):
+ """ OCBView reaction to leftMouseButtonRelease event. """
event = self.drag_edge(event, 'release')
if event is not None:
super().mouseReleaseEvent(event)
def rightMouseButtonPress(self, event: QMouseEvent):
+ """ OCBView reaction to rightMouseButtonPress event. """
event = self.drag_scene(event, "press")
super().mousePressEvent(event)
def rightMouseButtonRelease(self, event: QMouseEvent):
+ """ OCBView reaction to rightMouseButtonRelease event. """
event = self.drag_scene(event, "release")
super().mouseReleaseEvent(event)
self.setDragMode(QGraphicsView.DragMode.RubberBandDrag)
def wheelEvent(self, event: QWheelEvent):
- """ Handles zooming with mouse wheel """
+ """ Handles zooming with mouse wheel events. """
if Qt.Modifier.CTRL == int(event.modifiers()):
# calculate zoom
if event.angleDelta().y() > 0:
@@ -136,30 +155,23 @@ def wheelEvent(self, event: QWheelEvent):
super().wheelEvent(event)
def deleteSelected(self):
+ """ Delete selected items from the current scene. """
scene = self.scene()
for selected_item in scene.selectedItems():
selected_item.remove()
scene.history.checkpoint("Delete selected elements", set_modified=True)
- def bring_forward(self, event: QMouseEvent):
- """ When a codeblock is selected, it will be drawn in front of other blocks """
- item_at_click = self.itemAt(event.pos())
- if item_at_click is None:
- return event
-
- while item_at_click.parentItem() is not None:
- if isinstance(item_at_click,OCBBlock):
- break
- item_at_click = item_at_click.parentItem()
+ def bring_block_forward(self, block: OCBBlock):
+ """ Move the selected block in front of other blocks.
- if isinstance(item_at_click, OCBBlock):
- if self.currentSelectedBlock is not None and not isdeleted(self.currentSelectedBlock):
- self.currentSelectedBlock.setZValue(0)
- item_at_click.setZValue(1)
- self.currentSelectedBlock = item_at_click
-
- return event # This is never considered as a handling of the event.
+ Args:
+ block: Block to bring forward.
+ """
+ if self.currentSelectedBlock is not None and not isdeleted(self.currentSelectedBlock):
+ self.currentSelectedBlock.setZValue(0)
+ block.setZValue(1)
+ self.currentSelectedBlock = block
def drag_scene(self, event: QMouseEvent, action="press"):
""" Drag the scene around. """
@@ -209,7 +221,19 @@ def drag_edge(self, event: QMouseEvent, action="press"):
return event
def set_mode(self, mode:str):
+ """ Change the view mode.
+
+ Args:
+ mode: Mode key to change to, must in present in knowed MODES.
+
+ """
self.mode = MODES[mode]
def is_mode(self, mode:str):
+ """ Return True if the view is in the given mode.
+
+ Args:
+ mode: Mode key to compare to, must in present in knowed MODES.
+
+ """
return self.mode == MODES[mode]
diff --git a/opencodeblocks/graphics/window.py b/opencodeblocks/graphics/window.py
index 6e959a8b..c960e362 100644
--- a/opencodeblocks/graphics/window.py
+++ b/opencodeblocks/graphics/window.py
@@ -194,6 +194,7 @@ def closeEvent(self, event:QEvent):
event.ignore()
def isModified(self) -> bool:
+ """ Return True if the scene has been modified, False otherwise. """
return self.centralWidget().scene.has_been_modified
def maybeSave(self) -> bool: