Skip to content

Commit

Permalink
Fix toolbar logics, change clipboard paste logics
Browse files Browse the repository at this point in the history
  • Loading branch information
don4get committed Apr 11, 2020
1 parent 8c0eea2 commit 30366bb
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 47 deletions.
1 change: 1 addition & 0 deletions examples/example_calculator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..")) # noqa: E402


if __name__ == "__main__":
app = QApplication(sys.argv)

Expand Down
3 changes: 2 additions & 1 deletion nodedge/editor_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@


class EditorWidget(QWidget):
SceneClass = Scene
"""
:class:`~nodedge.editor_widget.EditorWidget` class
Expand Down Expand Up @@ -62,7 +63,7 @@ def initUI(self):
self.layout.setContentsMargins(0, 0, 0, 0)

self.setLayout(self.layout)
self.scene: Scene = Scene()
self.scene: Scene = self.__class__.SceneClass()
self.view: GraphicsView = GraphicsView(self.scene.graphicsScene, self)
self.layout.addWidget(self.view)

Expand Down
10 changes: 5 additions & 5 deletions nodedge/graphics_node_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def initUI(self):
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)

self.layout.addWidget(AckTextEdit("X3"))
self.layout.addWidget(TextEdit("X3"))

def setEditingFlag(self, value: bool) -> None:
"""
Expand All @@ -56,10 +56,10 @@ def setEditingFlag(self, value: bool) -> None:
If you are handling keyPress events by default Qt Window's shortcuts and ``QActions``, you will not
probably need to use this method
Helper function which sets editingFlag inside :py:class:`~nodedge.graphics_view.QDMGraphicsView` class.
Helper function which sets editingFlag inside :py:class:`~nodedge.graphics_view.GraphicsView` class.
This is a helper function to handle keys inside nodes with ``QLineEdits`` or ``QTextEdits`` (you can
use overridden :py:class:`QDMTextEdit` class) and with QGraphicsView class method ``keyPressEvent``.
use overridden :py:class:`TextEdit` class) and with QGraphicsView class method ``keyPressEvent``.
:param value: new value for editing flag
"""
Expand Down Expand Up @@ -93,14 +93,14 @@ def deserialize(
return True


class AckTextEdit(QTextEdit):
class TextEdit(QTextEdit):
"""
.. note::
This class is example of ``QTextEdit`` modification to be able to handle `Delete` key with overridden
Qt's ``keyPressEvent`` (when not using ``QActions`` in menu or toolbar)
overridden ``QTextEdit`` which sends notification about being edited to parent's container :py:class:`QDMNodeContentWidget`
overridden ``QTextEdit`` which sends notification about being edited to parent's container :py:class:`GraphicsNodeContent`
"""

def focusInEvent(self, event: QFocusEvent) -> None:
Expand Down
31 changes: 21 additions & 10 deletions nodedge/graphics_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
Graphics socket module containing :class:`~nodedge.graphics_socket.GraphicsSocket` class.
"""

from PyQt5.QtCore import QRectF
from PyQt5.QtCore import QRectF, Qt
from PyQt5.QtGui import QBrush, QColor, QPen
from PyQt5.QtWidgets import QGraphicsItem

SOCKET_COLORS = [
QColor("#FFFF7700"),
QColor("#FF52e220"),
QColor("#FF0056a6"),
QColor("#FFa86db1"),
QColor("#FFb54747"),
QColor("#FFdbe220"),
QColor("#FF888888"),
]


class GraphicsSocket(QGraphicsItem):
"""
Expand Down Expand Up @@ -42,15 +52,7 @@ def initStyle(self) -> None:
"""
Initialize ``QObjects`` like ``QColor``, ``QPen`` and ``QBrush``
"""
self._colors = [
QColor("#FFFF7700"),
QColor("#FF52e220"),
QColor("#FF0056a6"),
QColor("#FFa86db1"),
QColor("#FFdbe220"),
]

self._color_background = self._colors[self.socketColor]
self._color_background = GraphicsSocket.getSocketColor(self.socketColor)
self._color_outline = QColor("#FF000000")

self._pen = QPen(self._color_outline)
Expand All @@ -65,6 +67,15 @@ def initSizes(self) -> None:
self.radius = 6.0
self.outline_width = 1.0

@staticmethod
def getSocketColor(key):
"""Returns the ``QColor`` for this ``key``"""
if type(key) == int:
return SOCKET_COLORS[key]
elif type(key) == str:
return QColor(key)
return Qt.transparent

def paint(self, painter, QStyleOptionGraphicsItem, widget=None):
"""
Paint a circle
Expand Down
1 change: 1 addition & 0 deletions nodedge/graphics_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def leftMouseButtonPress(self, event: QMouseEvent):
"""
try:
item = self.getItemAtClick(event)
self.__logger.debug(f"Selected object class: {item.__class__.__name__}")

self.lastLMBClickScenePos = self.mapToScene(event.pos())

Expand Down
11 changes: 6 additions & 5 deletions nodedge/mdi_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def currentEditorWidget(self) -> EditorWidget:
if (
activeSubWindow
and activeSubWindow.widget
and isinstance(activeSubWindow.widget(), (EditorWidget))
and isinstance(activeSubWindow.widget(), EditorWidget)
):
self.lastActiveEditorWidget = activeSubWindow.widget()
return typing.cast(EditorWidget, self.lastActiveEditorWidget)
Expand Down Expand Up @@ -96,7 +96,7 @@ def initUI(self):

self.createActions()
self.createMenus()
# self.createToolBars()
self.createToolBars()
self.createStatusBar()
self.updateMenus()

Expand All @@ -110,7 +110,7 @@ def createStatusBar(self):
"""
self.statusBar().showMessage("Ready")

# noinspection PyArgumentList
# noinspection PyArgumentList, PyAttributeOutsideInit
def createActions(self):
"""
Create `File`, `Edit` and `About` actions.
Expand Down Expand Up @@ -169,7 +169,7 @@ def createActions(self):
triggered=self.onNodesToolbarTriggered,
)
self.nodeToolbarAct.setCheckable(True)
self.nodeToolbarAct.setChecked(self.nodesDock.isVisible())
self.nodeToolbarAct.setChecked(True) # self.nodesDock.isVisible()

self.separatorAct = QAction(self)
self.separatorAct.setSeparator(True)
Expand All @@ -181,9 +181,10 @@ def createActions(self):
triggered=self.about,
)

# noinspection PyAttributeOutsideInit
def createToolBars(self):
"""
Create the `File` and `Edit` toolbar contaning few of their menu actions.
Create the `File` and `Edit` toolbar containing few of their menu actions.
"""
self.fileToolBar = self.addToolBar("File")
self.fileToolBar.addAction(self.newAct)
Expand Down
73 changes: 59 additions & 14 deletions nodedge/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ def __init__(self):
self._itemSelectedListeners = []
self._itemsDeselectedListeners = []

self._silentSelectionEvents = False

# Store callback for retrieving the nodes classes
self.nodeClassSelector = None

Expand Down Expand Up @@ -118,28 +120,67 @@ def view(self):
"""
return self.graphicsScene.views()[0]

def onItemSelected(self):
"""Handle Item selection and trigger event `Item Selected`"""
@property
def silentSelectionEvents(self):
""""
If this property is true, do not trigger onItemSelected when an item is selected
:return: True is onItemSelected is not triggered when an item is selected
:rtype: ``bool``
"""
return self._silentSelectionEvents

@silentSelectionEvents.setter
def silentSelectionEvents(self, value: bool):
if self._silentSelectionEvents != value:
self._silentSelectionEvents = value

def onItemSelected(self, silent: bool = False):
"""
Handle Item selection and trigger event `Item Selected`
:param silent: If ``True`` scene's onItemSelected won't be called and history stamp not stored
:type silent: ``bool``
"""
if self._silentSelectionEvents:
return

selectedItems = self.selectedItems
if selectedItems != self._lastSelectedItems:
self.lastSelectedItems = selectedItems
self.history.store("Change selection")

for callback in self._itemSelectedListeners:
callback()
self.__logger.debug("Everything done after item has been selected")
if not silent:
self.history.store("Change selection")
for callback in self._itemSelectedListeners:
callback()

def onItemsDeselected(self, silent: bool = False):
"""
Handle Items deselection and trigger event `Items Deselected`
def onItemsDeselected(self):
"""Handle Items deselection and trigger event `Items Deselected`"""
:param silent: If ``True`` scene's onItemsDeselected won't be called and history stamp not stored
:type silent: ``bool``
"""
self.resetLastSelectedStates()
if self.lastSelectedItems:
self.lastSelectedItems = []
self.history.store("Deselect everything")

self.__logger.debug("Everything done after all items have been deselected")
if not silent:
self.history.store("Deselect everything")
for callback in self._itemsDeselectedListeners:
callback()

for callback in self._itemsDeselectedListeners:
callback()
def doDeselectItems(self, silent: bool = False) -> None:
"""
Deselects everything in scene
:param silent: If ``True`` scene's onItemsDeselected won't be called
:type silent: ``bool``
"""
for item in self.selectedItems:
item.setSelected(False)
if not silent:
self.onItemsDeselected()

def addHasBeenModifiedListener(self, callback: Callable[[], None]):
"""
Expand Down Expand Up @@ -216,7 +257,9 @@ def removeNode(self, nodeToRemove: Node):
if nodeToRemove in self.nodes:
self.nodes.remove(nodeToRemove)
else:
self.__logger.warning(f"Trying to remove {nodeToRemove} from {self}.")
self.__logger.warning(
f"Trying to remove {nodeToRemove} from {self} but is it not in the node list."
)

def removeEdge(self, edgeToRemove: Edge):
"""Remove :class:`~nodedge.edge.Edge` from this `Scene`
Expand All @@ -227,7 +270,9 @@ def removeEdge(self, edgeToRemove: Edge):
if edgeToRemove in self.edges:
self.edges.remove(edgeToRemove)
else:
self.__logger.warning(f"Trying to remove {edgeToRemove} from {self}.")
self.__logger.warning(
f"Trying to remove {edgeToRemove} from {self} but is it not is the edge list."
)

def clear(self):
"""Remove all `Nodes` from this `Scene`. This causes also to remove all `Edges`"""
Expand Down
39 changes: 29 additions & 10 deletions nodedge/scene_clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import logging
from collections import OrderedDict

from PyQt5.QtCore import QPointF

from nodedge.edge import Edge
from nodedge.graphics_edge import GraphicsEdge

Expand Down Expand Up @@ -102,32 +104,49 @@ def deserialize(self, data):
minY = y
if y > maxY:
maxY = y
boundedBoxCenterX = (minX + maxX) / 2
boundedBoxCenterY = (minY + maxY) / 2

# center = view.mapToScene(view.rect().center)
# Add width and height of a node
# TODO: Do not use hard coded node width and height
maxX -= 180
maxY += 100

relativeCenterX = (minX + maxX) / 2 - minX
relativeCenterY = (minY + maxY) / 2 - minY

self.__logger.debug(f"Mouse pos: X:{mouseScenePos.x()} Y:{mouseScenePos.y()}")
self.__logger.debug(f"Copied boudaries: X:[{minX, maxY}] Y:[{minY, maxY}]")
self.__logger.debug(
f"Relative center: X:{relativeCenterX} Y:{relativeCenterY}"
)

# create each node
createdNodes = []

# Calculate the offset of newly created blocks
offsetX = mouseScenePos.x() - boundedBoxCenterX
offsetY = mouseScenePos.y() - boundedBoxCenterY
self.scene.silentSelectionEvents = True

self.scene.doDeselectItems()

# Create each node

for nodeData in data["nodes"]:
newNode = self.scene.getNodeClassFromData(nodeData)(self.scene)
newNode.deserialize(nodeData, hashmap, restoreId=False)
createdNodes.append(newNode)

# Reajust the new node position
pos = newNode.pos
newNode.pos = (pos.x() + offsetX, pos.y() + offsetY)
# Readjust the new node position
newNode.pos = newNode.pos + mouseScenePos - QPointF(minX, minY)
newNode.isSelected = True

# Create each edge
if "edges" in data:
for edgeData in data["edges"]:
newEdge = Edge(self.scene)
newEdge.deserialize(edgeData, hashmap, restoreId=False)

self.scene.silentSelectionEvents = False

# Store history
self.scene.history.store("Paste items in scene.")

self.__logger.debug(f"Deserializing from clipboard: {data}")

return createdNodes
10 changes: 8 additions & 2 deletions nodedge/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,21 @@
pp = PrettyPrinter(indent=4).pprint


def dumpException(e):
def dumpException(e=None, file=None):
"""
Print out an exception message with the traceback to the console.
:param e: Exception to print out
:type e: Exception
:param file: optional, file where the expection is dumped
:type file: ``str``
"""
logging.warning(f"{e.__class__.__name__} Exception: {e}")
traceback.print_tb(e.__traceback__)
if file is not None:
traceback.print_tb(e.__traceback__, file=file)
else:
traceback.print_exc()


def loadStyleSheet(fileName):
Expand Down

0 comments on commit 30366bb

Please sign in to comment.