Handle locking/unlocking items independently from the layer position.

This commit is contained in:
grossmj
2019-03-02 16:26:41 +07:00
parent 198cf833e9
commit 740e9bab87
17 changed files with 101 additions and 116 deletions

View File

@@ -466,13 +466,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
if not event.modifiers() & QtCore.Qt.ControlModifier:
for it in self.scene().items():
it.setSelected(False)
if item.zValue() < 0:
item.setFlag(item.ItemIsSelectable, True)
item.setSelected(True)
self._showDeviceContextualMenu(QtGui.QCursor.pos())
if not sip.isdeleted(item) and item.zValue() < 0:
item.setFlag(item.ItemIsSelectable, False)
else:
self._showDeviceContextualMenu(QtGui.QCursor.pos())
# when more than one item is selected display the contextual menu even if mouse is not above an item
@@ -1461,11 +1456,7 @@ class GraphicsView(QtWidgets.QGraphicsView):
for item in self.scene().selectedItems():
if item.parentItem() is None:
current_zvalue = item.zValue()
if not (item.flags() & QtWidgets.QGraphicsItem.ItemIsMovable):
log.error("Cannot move object to a upper layer because it is locked")
continue
item.setZValue(current_zvalue + 1)
item.setZValue(item.zValue() + 1)
item.updateNode()
item.update()
@@ -1477,17 +1468,10 @@ class GraphicsView(QtWidgets.QGraphicsView):
for item in self.scene().selectedItems():
if item.parentItem() is None:
current_zvalue = item.zValue()
if not (item.flags() & QtWidgets.QGraphicsItem.ItemIsMovable):
log.error("Cannot move object to a lower layer because it is locked")
continue
item.setZValue(current_zvalue - 1)
item.setZValue(item.zValue() - 1)
item.updateNode()
item.update()
if item.zValue() == -1:
self._background_warning_msgbox.showMessage("Object moved to a background layer. You will now have to use the right-click action to select this object in the future and raise it to layer 0 to be able to move it")
def lockActionSlot(self):
"""
Slot to receive events from the lock action in the
@@ -1495,8 +1479,11 @@ class GraphicsView(QtWidgets.QGraphicsView):
"""
for item in self.scene().selectedItems():
if not isinstance(item, LinkItem):
item.setZValue(-(item.zValue() + 1))
if not isinstance(item, LinkItem) and not isinstance(item, LabelItem):
if item.locked() is True:
item.setLocked(False)
else:
item.setLocked(True)
if item.parentItem() is None:
item.updateNode()
item.update()
@@ -1557,9 +1544,9 @@ class GraphicsView(QtWidgets.QGraphicsView):
pos = self.mapToScene(pos)
return TemplateManager().instance().createNodeFromTemplateId(self._topology.project(), template_id, pos.x(), pos.y())
def createNodeItem(self, node, symbol, x, y, z):
def createNodeItem(self, node, symbol, x, y):
node.setSymbol(symbol)
node.setPos(x, y, z)
node.setPos(x, y)
node_item = NodeItem(node)
self.scene().addItem(node_item)
@@ -1582,18 +1569,18 @@ class GraphicsView(QtWidgets.QGraphicsView):
if self._main_window and not sip.isdeleted(self._main_window):
QtWidgets.QMessageBox.critical(self._main_window, name, message.strip())
def createDrawingItem(self, type, x, y, z, rotation=0, svg=None, drawing_id=None):
def createDrawingItem(self, type, x, y, z, locked=False, rotation=0, svg=None, drawing_id=None):
if type == "ellipse":
item = EllipseItem(pos=QtCore.QPoint(x, y), z=z, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
item = EllipseItem(pos=QtCore.QPoint(x, y), z=z, locked=locked, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
elif type == "rect":
item = RectangleItem(pos=QtCore.QPoint(x, y), z=z, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
item = RectangleItem(pos=QtCore.QPoint(x, y), z=z, locked=locked, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
elif type == "line":
item = LineItem(pos=QtCore.QPoint(x, y), dst=QtCore.QPoint(200, 0), z=z, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
item = LineItem(pos=QtCore.QPoint(x, y), dst=QtCore.QPoint(200, 0), z=z, locked=locked, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
elif type == "image":
item = ImageItem(pos=QtCore.QPoint(x, y), z=z, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
item = ImageItem(pos=QtCore.QPoint(x, y), z=z, rotation=rotation, locked=locked, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
elif type == "text":
item = TextItem(pos=QtCore.QPoint(x, y), z=z, rotation=rotation, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
item = TextItem(pos=QtCore.QPoint(x, y), z=z, rotation=rotation, locked=locked, project=self._topology.project(), drawing_id=drawing_id, svg=svg)
if drawing_id is None:
item.create()

View File

@@ -41,9 +41,10 @@ class DrawingItem:
Base class for non emulation item
"""
def __init__(self, project=None, pos=None, drawing_id=None, svg=None, z=0, rotation=0, **kws):
def __init__(self, project=None, pos=None, drawing_id=None, svg=None, z=0, locked=False, rotation=0, **kws):
self._id = drawing_id
self._deleting = False
self._locked = locked
if self._id is None:
self._id = str(uuid.uuid4())
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable | QtWidgets.QGraphicsItem.ItemIsFocusable | QtWidgets.QGraphicsItem.ItemIsSelectable | QtWidgets.QGraphicsItem.ItemSendsGeometryChanges)
@@ -65,6 +66,8 @@ class DrawingItem:
if rotation:
self.setRotation(rotation)
self.setLocked(locked)
def drawing_id(self):
return self._id
@@ -106,6 +109,7 @@ class DrawingItem:
return False
self.setPos(QtCore.QPoint(result["x"], result["y"]))
self.setZValue(result["z"])
self.setLocked(result["locked"])
self.setRotation(result["rotation"])
if "svg" in result:
self.fromSvg(result["svg"])
@@ -149,6 +153,7 @@ class DrawingItem:
"x": int(self.pos().x()),
"y": int(self.pos().y()),
"z": int(self.zValue()),
"locked": self._locked,
"rotation": int(self.rotation())
}
svg = self.toSvg()
@@ -158,23 +163,29 @@ class DrawingItem:
self._hash_svg = hash_svg
return data
def setZValue(self, value):
def locked(self):
"""
Sets a new Z value.
Is the drawing locked
"""
return self._locked
def setLocked(self, locked):
"""
Sets the locked value.
:param value: Z value
"""
QtWidgets.QGraphicsItem.setZValue(self, value)
if self.zValue() < 0:
if locked is True:
self.setFlag(self.ItemIsMovable, False)
else:
self.setFlag(self.ItemIsMovable, True)
self._locked = locked
def deleting(self):
"""
Is the link being deleted
Is the drawing being deleted
"""
return self._deleting

View File

@@ -22,7 +22,7 @@ Graphical representation of an ellipse on the QGraphicsScene.
import math
import xml.etree.ElementTree as ET
from ..qt import QtCore, QtGui, QtWidgets
from ..qt import QtWidgets
from .shape_item import ShapeItem
@@ -48,13 +48,6 @@ class EllipseItem(QtWidgets.QGraphicsEllipseItem, ShapeItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return ShapeItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the shape

View File

@@ -67,13 +67,6 @@ class ImageItem(QtSvg.QGraphicsSvgItem, DrawingItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return DrawingItem.setZValue(self, value)
def fromSvg(self, svg):
renderer = QImageSvgRenderer(svg)
self.setSharedRenderer(renderer)

View File

@@ -170,19 +170,6 @@ class LabelItem(QtWidgets.QGraphicsTextItem):
zval = str(int(self.zValue()))
painter.drawText(QtCore.QPointF(center.x(), center.y()), zval)
def setZValue(self, value):
"""
Sets a new Z value.
:param value: Z value
"""
super().setZValue(value)
if self.zValue() < 0:
self.setFlag(self.ItemIsMovable, False)
else:
self.setFlag(self.ItemIsMovable, True)
def setStyle(self, new_style):
"""
Set text style using a SVG style

View File

@@ -63,13 +63,6 @@ class LineItem(QtWidgets.QGraphicsLineItem, DrawingItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return DrawingItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the shape
@@ -126,8 +119,8 @@ class LineItem(QtWidgets.QGraphicsLineItem, DrawingItem):
:param event: QGraphicsSceneHoverEvent instance
"""
# objects on the background layer don't need cursors
if self.zValue() >= 0:
# locked objects don't need cursors
if not self.locked():
if self._isHorizontalLine():
if event.pos().x() > (self.line().x2() - self._border):
@@ -218,6 +211,6 @@ class LineItem(QtWidgets.QGraphicsLineItem, DrawingItem):
:param event: QGraphicsSceneHoverEvent instance
"""
# objects on the background layer don't need cursors
if self.zValue() >= 0:
# locked objects don't need cursors
if not self.locked():
self._graphics_view.setCursor(QtCore.Qt.ArrowCursor)

View File

@@ -50,6 +50,7 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
# link items connected to this node item.
self._links = []
self._symbol = None
self._locked = False
# says if the attached node has been initialized
# by the server.
@@ -78,7 +79,8 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges)
self.setAcceptHoverEvents(True)
# update z value and set proper flags - not movable in case z < 0
# update z value and locked state
self.setLocked(self._node.locked())
self.setZValue(self._node.z())
# connect signals to know about some events
@@ -272,7 +274,7 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
self.setSymbol(self._node.settings().get("symbol"))
self.setPos(self._node.settings().get("x", 0), self._node.settings().get("y", 0))
self.setZValue(self._node.settings().get("z", 0))
self.setLocked(self._node.settings().get("locked", False))
self._updateLabel()
# update the link tooltips in case the
@@ -388,7 +390,7 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
self._node_label.setStyle(style)
self._node_label.setRotation(label_data.get("rotation", 0))
if self._node.z() < 0:
if self._node.locked():
self._node_label.setFlag(self.ItemIsMovable, False)
if label_data["x"] is None:
@@ -531,7 +533,21 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
"""
super().setZValue(value)
if self.zValue() < 0:
for link in self._links:
link.adjust()
def locked(self):
return self._locked
def setLocked(self, locked):
"""
Sets the locked value.
:param value: Z value
"""
if locked is True:
self.setFlag(self.ItemIsMovable, False)
if self._node_label:
self._node_label.setFlag(self.ItemIsMovable, False)
@@ -541,6 +557,7 @@ class NodeItem(QtSvg.QGraphicsSvgItem):
self._node_label.setFlag(self.ItemIsMovable, True)
for link in self._links:
link.adjust()
self._locked = locked
def hoverEnterEvent(self, event):
"""

View File

@@ -46,13 +46,6 @@ class RectangleItem(QtWidgets.QGraphicsRectItem, ShapeItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return ShapeItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the shape

View File

@@ -147,8 +147,8 @@ class ShapeItem(DrawingItem):
:param event: QGraphicsSceneHoverEvent instance
"""
# objects on the background layer don't need cursors
if self.zValue() >= 0:
# locked objects don't need cursors
if not self.locked():
if event.pos().x() > (self.rect().right() - self._border):
self._graphics_view.setCursor(QtCore.Qt.SizeHorCursor)
elif event.pos().x() < (self.rect().left() + self._border):
@@ -167,8 +167,8 @@ class ShapeItem(DrawingItem):
:param event: QGraphicsSceneHoverEvent instance
"""
# objects on the background layer don't need cursors
if self.zValue() >= 0:
# locked objects don't need cursors
if not self.locked():
self._graphics_view.setCursor(QtCore.Qt.ArrowCursor)
def fromSvg(self, svg):

View File

@@ -111,13 +111,6 @@ class TextItem(QtWidgets.QGraphicsTextItem, DrawingItem):
super().paint(painter, option, widget)
self.drawLayerInfo(painter)
def setZValue(self, value):
"""
Sets Z value of the item
:param value: z layer
"""
return DrawingItem.setZValue(self, value)
def toSvg(self):
"""
Return an SVG version of the text

View File

@@ -44,6 +44,7 @@ from .settings import GENERAL_SETTINGS
from .items.node_item import NodeItem
from .items.link_item import LinkItem
from .items.shape_item import ShapeItem
from .items.label_item import LabelItem
from .topology import Topology
from .http_client import HTTPClient
from .progress import Progress
@@ -363,17 +364,14 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
"""
for item in self.uiGraphicsView.items():
if not isinstance(item, LinkItem):
if self.uiLockAllAction.isChecked() and item.zValue() >= 0:
item.setZValue(-(item.zValue() + 1))
if item.parentItem() is None:
item.updateNode()
item.update()
elif not self.uiLockAllAction.isChecked() and item.zValue() < 0:
item.setZValue(-(item.zValue() + 1))
if item.parentItem() is None:
item.updateNode()
item.update()
if not isinstance(item, LinkItem) and not isinstance(item, LabelItem):
if self.uiLockAllAction.isChecked() and not item.locked():
item.setLocked(True)
elif not self.uiLockAllAction.isChecked() and item.locked():
item.setLocked(False)
if item.parentItem() is None:
item.updateNode()
item.update()
def analyticsClient(self):
"""

View File

@@ -46,7 +46,8 @@ class Node(BaseNode):
self._settings = {"name": "",
"x": None,
"y": None,
"z": 1}
"z": 1,
"locked": False}
def settings(self):
"""
@@ -148,6 +149,13 @@ class Node(BaseNode):
return self._settings["z"]
def locked(self):
"""
Is the node locked
"""
return self._settings["locked"]
def setSymbol(self, symbol):
"""
Sets the symbol for this node.
@@ -373,6 +381,7 @@ class Node(BaseNode):
"x",
"y",
"z",
"locked",
"symbol",
"label",
"port_name_format",
@@ -529,7 +538,7 @@ class Node(BaseNode):
del result["properties"]
# Update common element of all nodes
for key in ["x", "y", "z", "symbol", "label", "console_host", "console", "console_type", "console_auto_start", "custom_adapters"]:
for key in ["x", "y", "z", "locked", "symbol", "label", "console_host", "console", "console_type", "console_auto_start", "custom_adapters"]:
if key in result:
self._settings[key] = result[key]
@@ -578,7 +587,8 @@ class Node(BaseNode):
data = {"x": int(node_item.pos().x()),
"y": int(node_item.pos().y()),
"z": int(node_item.zValue()),
"symbol": node_item.symbol()}
"symbol": node_item.symbol(),
"locked": node_item.locked()}
if node_item.label() is not None:
data["label"] = node_item.label().dump()

View File

@@ -541,7 +541,7 @@ It is your responsability to check if you have the right to distribute the image
node = node_module.instantiateNode(node_class, ComputeManager.instance().getCompute(node_data["compute_id"]), self._project)
node.createNodeCallback(node_data)
self._main_window.uiGraphicsView.createNodeItem(node, node_data["symbol"], node_data["x"], node_data["y"], node_data.get("z", 1))
self._main_window.uiGraphicsView.createNodeItem(node, node_data["symbol"], node_data["x"], node_data["y"])
def createLink(self, link_data):
source_port = None
@@ -597,7 +597,7 @@ It is your responsability to check if you have the right to distribute the image
except IndexError:
# If unknow we render it as a raw SVG image
type = "image"
self._main_window.uiGraphicsView.createDrawingItem(type, drawing_data["x"], drawing_data["y"], drawing_data["z"], rotation=drawing_data["rotation"], drawing_id=drawing_data["drawing_id"], svg=drawing_data["svg"])
self._main_window.uiGraphicsView.createDrawingItem(type, drawing_data["x"], drawing_data["y"], drawing_data["z"], locked=drawing_data["locked"], rotation=drawing_data["rotation"], drawing_id=drawing_data["drawing_id"], svg=drawing_data["svg"])
@staticmethod
def instance():

View File

@@ -110,6 +110,7 @@ def test_create(project, controller):
"x": 0,
"y": 0,
"z": 0,
"locked": rect.locked(),
"svg": rect.toSvg(),
"rotation": int(rect.rotation())
}

View File

@@ -75,6 +75,7 @@ def test_create(project, controller):
"x": 0,
"y": 0,
"z": 0,
"locked": rect.locked(),
"svg": rect.toSvg(),
"rotation": int(rect.rotation())
}

View File

@@ -158,6 +158,9 @@ def test_node_setGraphics(vpcs_device):
zValue=MagicMock(
return_value=2
),
locked=MagicMock(
return_value=False
),
symbol=MagicMock(
return_value="symbol.svg"
)
@@ -172,6 +175,7 @@ def test_node_setGraphics(vpcs_device):
vpcs_device.setSettingValue('x', 10)
vpcs_device.setSettingValue('y', 20)
vpcs_device.setSettingValue('z', 2)
vpcs_device.setSettingValue('locked', False)
vpcs_device.setSettingValue('symbol', "symbol.svg")
vpcs_device.setSettingValue('label', node.label().dump())

View File

@@ -40,13 +40,14 @@ def test_createDrawing_ellipse():
"x": 42,
"y": 12,
"z": 0,
"locked": False,
"rotation": 0,
"drawing_id": str(uuid.uuid4()),
"svg": "<svg height=\"105.0\" width=\"158.0\"><ellipse cx=\"79\" cy=\"52\" rx=\"79\" ry=\"53\" style=\"stroke-width:2;stroke:#000000;fill:#ffffff;\" /></svg>",
}
topology._main_window = MagicMock()
topology.createDrawing(shape_data)
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("ellipse", 42, 12, 0, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("ellipse", 42, 12, 0, locked=False, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])
def test_createDrawing_rect():
@@ -55,13 +56,14 @@ def test_createDrawing_rect():
"x": 42,
"y": 12,
"z": 0,
"locked": False,
"rotation": 0,
"drawing_id": str(uuid.uuid4()),
"svg": "<svg height=\"105.0\" width=\"158.0\"><rect/></svg>",
}
topology._main_window = MagicMock()
topology.createDrawing(shape_data)
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("rect", 42, 12, 0, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("rect", 42, 12, 0, locked=False, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])
def test_createDrawing_text():
@@ -70,13 +72,14 @@ def test_createDrawing_text():
"x": 42,
"y": 12,
"z": 0,
"locked": False,
"rotation": 0,
"drawing_id": str(uuid.uuid4()),
"svg": "<svg height=\"105.0\" width=\"158.0\"><text/></svg>",
}
topology._main_window = MagicMock()
topology.createDrawing(shape_data)
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("text", 42, 12, 0, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("text", 42, 12, 0, locked=False, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])
def test_createDrawing_svg():
@@ -88,11 +91,12 @@ def test_createDrawing_svg():
"x": 42,
"y": 12,
"z": 0,
"locked": False,
"rotation": 0,
"drawing_id": str(uuid.uuid4()),
"svg": "<svg height=\"105.0\" width=\"158.0\"><rect><line/></rect></svg>",
}
topology._main_window = MagicMock()
topology.createDrawing(shape_data)
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("image", 42, 12, 0, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])
topology._main_window.uiGraphicsView.createDrawingItem.assert_called_with("image", 42, 12, 0, locked=False, rotation=0, svg=shape_data["svg"], drawing_id=shape_data["drawing_id"])