Skip to content

Commit

Permalink
Clean scene and history
Browse files Browse the repository at this point in the history
  • Loading branch information
don4get committed Jun 18, 2020
1 parent 784641b commit 3f6a900
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 45 deletions.
51 changes: 48 additions & 3 deletions nodedge/blocks/block.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
"""Block module containing :class:`~nodedge.block.Block` class. """

import logging
from collections import OrderedDict
from typing import Optional

from nodedge.blocks.graphics_block import GraphicsBlock
Expand All @@ -9,6 +13,12 @@


class Block(Node):
"""
:class:`~nodedge.block.Block` class
A block is node which can be evaluated to produce an output.
"""

iconPath = ""
operationTitle = "Undefined"
operationCode = 0
Expand All @@ -33,11 +43,20 @@ def __init__(self, scene, inputSocketTypes=(2, 2), outputSocketTypes=(1,)):

# noinspection PyAttributeOutsideInit
def initSettings(self):
"""
Initialize the location of the input and output sockets.
"""
super().initSettings()
self._inputSocketPosition = SocketLocation.LEFT_CENTER
self._outputSocketPosition = SocketLocation.RIGHT_CENTER

def onInputChanged(self, socket: Optional[Socket] = None):
def onInputChanged(self, socket: Optional[Socket] = None) -> None:
"""
Called when the value of an input has changed.
:param socket: the socket on which the input has changed
:return: ``None``
"""
self.__logger.debug(f"New edge: {socket}")
self.isDirty = True
self.eval()
Expand Down Expand Up @@ -87,12 +106,19 @@ def eval(self, index=0):
# self.markChildrenDirty()
# self.evalChildren()

def serialize(self):
def serialize(self) -> OrderedDict:
res = super().serialize()
res["operationCode"] = self.__class__.operationCode
return res

def deserialize(self, data, hashmap=None, restoreId=True):
def deserialize(
self,
data: dict,
hashmap: Optional[dict] = None,
restoreId: bool = True,
*args,
**kwargs,
):
if hashmap is None:
hashmap = {}
res = super().deserialize(data, hashmap, restoreId)
Expand All @@ -102,12 +128,31 @@ def deserialize(self, data, hashmap=None, restoreId=True):


class EvaluationError(Exception):
"""
:class:`~nodedge.block.EvaluationError` class
If a not cannot be evaluated, raise this error.
"""

pass


class MissInputError(EvaluationError):
"""
:class:`~nodedge.block.MissInputError` class
If an input is missing to a block, preventing it to be evaluated, raise this error.
"""

pass


class RedundantInputError(EvaluationError):
"""
:class:`~nodedge.block.RedundantInputError` class
If two different inputs are connected to a single input socket of a block,
raise this error.
"""

pass
20 changes: 19 additions & 1 deletion nodedge/blocks/graphics_input_block_content.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
"""
Graphics input block content module containing
:class:`~nodedge.graphics_input_block_content.GraphicsInputBlockContent` class.
"""

from typing import Optional

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QLineEdit

Expand All @@ -6,6 +13,10 @@


class GraphicsInputBlockContent(GraphicsNodeContent):
"""
:class:`~nodedge.graphics_input_block_content.GraphicsInputBlockContent` class
"""

# noinspection PyAttributeOutsideInit
def initUI(self):
self.edit = QLineEdit("1", self)
Expand All @@ -19,7 +30,14 @@ def serialize(self):
res["value"] = self.edit.text()
return res

def deserialize(self, data, hashmap=None, restoreId=False):
def deserialize(
self,
data: dict,
hashmap: Optional[dict] = None,
restoreId: bool = False,
*args,
**kwargs
):
if hashmap is None:
hashmap = {}
res = super().deserialize(data, hashmap, restoreId)
Expand Down
13 changes: 10 additions & 3 deletions nodedge/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __init__(
super().__init__()

self.__logger = logging.getLogger(__file__)
self.__logger.setLevel(logging.DEBUG)
self.__logger.setLevel(logging.INFO)

# Default initialization
self._sourceSocket: Optional[Socket] = None
Expand Down Expand Up @@ -236,7 +236,7 @@ def getOtherSocket(self, knownSocket: "Socket"):
self.sourceSocket if knownSocket == self.targetSocket else self.targetSocket
)

def removeFromSockets(self):
def removeFromSockets(self) -> None:
"""
Set start and end :class:`~nodedge.socket.Socket` to ``None``
"""
Expand Down Expand Up @@ -319,7 +319,14 @@ def serialize(self):
]
)

def deserialize(self, data, hashmap=None, restoreId=True):
def deserialize(
self,
data: dict,
hashmap: Optional[dict] = None,
restoreId: bool = True,
*args,
**kwargs,
):
if hashmap is None:
hashmap = {}
if restoreId:
Expand Down
5 changes: 3 additions & 2 deletions nodedge/edge_dragging.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class EdgeDraggingMode(IntEnum):
class EdgeDragging:
""":class:`~nodedge.edge_dragging.EdgeDragging` class ."""

def __init__(self, graphicsView: "GraphicsView"): # type: ignore
def __init__(self, graphicsView: "GraphicsView") -> None: # type: ignore
self.graphicsView = graphicsView
self.dragEdge: Optional[Edge] = None
self.dragStartSocket: Optional[Socket] = None
Expand All @@ -50,6 +50,8 @@ def startEdgeDragging(self, graphicsSocket: GraphicsSocket):
graphicsSocket.socket,
edgeType=EdgeType.BEZIER,
)
self.dragEdge.graphicsEdge.makeUnselectable()

except Exception as e:
dumpException(e)

Expand Down Expand Up @@ -86,7 +88,6 @@ def endEdgeDragging(self, graphicsSocket: GraphicsSocket):
self.graphicsView.graphicsScene.scene,
self.dragStartSocket,
graphicsSocket.socket,
edgeType=EdgeType.CIRCUIT,
)
graphicsSocket.socket.addEdge(newEdge)
self.__logger.debug(
Expand Down
8 changes: 8 additions & 0 deletions nodedge/graphics_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,14 @@ def setColorFromSockets(self) -> bool:
dumpException(e)
return True

def makeUnselectable(self):
"""
Used for :class:`~nodedge.edge_dragging.EdgeDragging` to disable click
detection over this graphics item.
"""
self.setFlag(QGraphicsItem.ItemIsSelectable, False)
self.setAcceptHoverEvents(False)


class GraphicsEdgeDirect(GraphicsEdge):
"""
Expand Down
14 changes: 13 additions & 1 deletion nodedge/graphics_node_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ def serialize(self) -> OrderedDict:
return OrderedDict([])

def deserialize(
self, data: dict, hashmap: Optional[dict] = None, restoreId: bool = False
self,
data: dict,
hashmap: Optional[dict] = None,
restoreId: bool = False,
*args,
**kwargs
) -> bool:
""" Default deserialize method.
Expand Down Expand Up @@ -137,6 +142,13 @@ def focusOutEvent(self, event: QFocusEvent) -> None:


class GraphicsNodeContentProxy(QGraphicsProxyWidget):
"""
:class:`~nodedge.graphics_node_content.GraphicsNodeContentProxy` class.
It is a ``QGraphicsProxyWidget`` around the
:class:`~nodedge.graphics_node_content.GraphicsNodeContent`.
"""

def __init__(self, graphicsNodeParent: "GraphicsNode") -> None: # type: ignore
super().__init__(graphicsNodeParent)
self.setWidget(graphicsNodeParent.content)
10 changes: 9 additions & 1 deletion nodedge/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Node(Serializable):
GraphicsNodeClass = GraphicsNode
GraphicsNodeContentClass = GraphicsNodeContent
SocketClass = Socket
contentLabelObjectName = "undefined"

def __init__(
self,
Expand Down Expand Up @@ -577,7 +578,14 @@ def serialize(self) -> OrderedDict:
]
)

def deserialize(self, data, hashmap=None, restoreId=True) -> bool:
def deserialize(
self,
data: dict,
hashmap: Optional[dict] = None,
restoreId: bool = True,
*args,
**kwargs,
):
if hashmap is None:
hashmap = {}
try:
Expand Down
49 changes: 33 additions & 16 deletions nodedge/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
class Scene(Serializable):
""":class:`~nodedge.scene.Scene` class"""

def __init__(self):
def __init__(self) -> None:
"""
:Instance Attributes:
Expand All @@ -44,22 +44,22 @@ def __init__(self):
self.__logger = logging.getLogger(__file__)
self.__logger.setLevel(logging.INFO)

self.sceneWidth = 64000
self.sceneHeight = 64000
self.sceneWidth: int = 64000
self.sceneHeight: int = 64000

self.history = SceneHistory(self)
self.clipboard = SceneClipboard(self)

self._isModified: bool = False
self.isModified = False
self.isModified: bool = False

self._lastSelectedItems = []
self._lastSelectedItems: List[QGraphicsItem] = []

self._hasBeenModifiedListeners = []
self._itemSelectedListeners = []
self._itemsDeselectedListeners = []
self._hasBeenModifiedListeners: List[Callable] = []
self._itemSelectedListeners: List[Callable] = []
self._itemsDeselectedListeners: List[Callable] = []

self._silentSelectionEvents = False
self._silentSelectionEvents: bool = False

# Store callback for retrieving the nodes classes
self.nodeClassSelector = None
Expand All @@ -69,6 +69,9 @@ def __init__(self):
self.graphicsScene.itemSelected.connect(self.onItemSelected)
self.graphicsScene.itemsDeselected.connect(self.onItemsDeselected)

# current filename assigned to this scene
self.filename: Optional[str] = None

@property
def isModified(self) -> bool:
"""
Expand All @@ -94,11 +97,18 @@ def isModified(self, value: bool):
self._isModified = value

@property
def lastSelectedItems(self):
def lastSelectedItems(self) -> List[QGraphicsItem]:
"""
Returns last selected graphics items.
This property is used to detect if selection has changed.
:return: Last selected items
:rtype: ``list[QGraphicsItem]``
"""
return self._lastSelectedItems

@lastSelectedItems.setter
def lastSelectedItems(self, value: bool):
def lastSelectedItems(self, value: List[QGraphicsItem]):
if value != self._lastSelectedItems:
self._lastSelectedItems = value

Expand All @@ -111,7 +121,7 @@ def selectedItems(self) -> List[QGraphicsItem]:
:rtype: list[QGraphicsItem]
"""

return cast(List[QGraphicsItem], self.graphicsScene.selectedItems())
return self.graphicsScene.selectedItems()

@property
def view(self) -> GraphicsView:
Expand Down Expand Up @@ -235,7 +245,7 @@ def addDropListener(self, callback: Callable[[QDropEvent], None]):
"""
self.view.addDropListener(callback)

def resetLastSelectedStates(self):
def resetLastSelectedStates(self) -> None:
"""Resets internal `selected flags` in all `Nodes` and `Edges` in the `Scene`"""

for node in self.nodes:
Expand Down Expand Up @@ -287,7 +297,7 @@ def removeEdge(self, edgeToRemove: Edge):
f"edge list. "
)

def clear(self):
def clear(self) -> None:
"""Remove all `Nodes` from this `Scene`. This causes also to remove all
`Edges` """
while len(self.nodes) > 0:
Expand All @@ -310,6 +320,7 @@ def saveToFile(self, filename):
self.__logger.info(f"Saving to {filename} was successful.")

self.isModified = False
self.filename = filename

def loadFromFile(self, filename):
"""
Expand All @@ -320,12 +331,13 @@ def loadFromFile(self, filename):
:raises: :class:`~nodedge.scene.InvalidFile` if there was an error
decoding JSON file.
"""
with open(filename, "r") as file:
with open(filename) as file:
rawData = file.read()
try:
data = json.loads(rawData, encoding="utf-8")
self.deserialize(data)
self.isModified = False
self.filename = filename
except json.JSONDecodeError:
raise InvalidFile(
f"{os.path.basename(filename)} is not a valid JSON file"
Expand All @@ -352,7 +364,12 @@ def serialize(self) -> OrderedDict:
)

def deserialize(
self, data: dict, hashmap: Optional[dict] = None, restoreId: bool = True
self,
data: dict,
hashmap: Optional[dict] = None,
restoreId: bool = True,
*args,
**kwargs,
) -> bool:
try:
if hashmap is None:
Expand Down

0 comments on commit 3f6a900

Please sign in to comment.