From 172569e664d01ebd9f5f598a6e20eccc0bdf39b5 Mon Sep 17 00:00:00 2001 From: ilgar Date: Sat, 17 Aug 2019 20:59:15 +0300 Subject: [PATCH] New while loop. Convex hull stuff moved to own class, to be inherited by nodes. --- .../PyFlowBase/Factories/UINodeFactory.py | 12 ++- .../Packages/PyFlowBase/Nodes/forLoopEnd.py | 6 +- PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py | 65 ++++++++++++++ .../PyFlowBase/Nodes/whileLoopBegin.py | 85 ++++++++++++++++++ .../Packages/PyFlowBase/UI/UICommentNode.py | 17 ---- .../PyFlowBase/UI/UIForLoopBeginNode.py | 90 +++---------------- ...QimageDisplay.py => UIImageDisplayNode.py} | 6 +- .../PyFlowBase/UI/UIWhileLoopBeginNode.py | 58 ++++++++++++ PyFlow/Packages/PyFlowBase/UI/UIstickyNote.py | 16 ++-- PyFlow/Packages/PyFlowBase/__init__.py | 6 +- PyFlow/UI/Canvas/IConvexHullBackDrop.py | 76 ++++++++++++++++ PyFlow/UI/Canvas/Painters.py | 7 -- PyFlow/UI/Canvas/UINodeBase.py | 2 +- PyFlow/UI/Canvas/loopBackDrop.py | 50 +++++++++++ docs/source/PyFlow.Packages.PyFlowBase.UI.rst | 8 +- 15 files changed, 376 insertions(+), 128 deletions(-) create mode 100644 PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py create mode 100644 PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py rename PyFlow/Packages/PyFlowBase/UI/{UIQimageDisplay.py => UIImageDisplayNode.py} (89%) create mode 100644 PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py create mode 100644 PyFlow/UI/Canvas/IConvexHullBackDrop.py create mode 100644 PyFlow/UI/Canvas/loopBackDrop.py diff --git a/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py b/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py index 3191d8263..abac4fa50 100644 --- a/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py +++ b/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py @@ -36,16 +36,17 @@ from PyFlow.Packages.PyFlowBase.Nodes.makeAnyDict import makeAnyDict from PyFlow.Packages.PyFlowBase.Nodes.forLoopBegin import forLoopBegin +from PyFlow.Packages.PyFlowBase.Nodes.whileLoopBegin import whileLoopBegin from PyFlow.Packages.PyFlowBase.Nodes.imageDisplay import imageDisplay -from PyFlow.Packages.PyFlowBase.UI.UIQimageDisplay import UIQimageDisplay +from PyFlow.Packages.PyFlowBase.UI.UIImageDisplayNode import UIImageDisplayNode from PyFlow.Packages.PyFlowBase.UI.UISwitchOnStringNode import UISwitchOnString from PyFlow.Packages.PyFlowBase.UI.UIGetVarNode import UIGetVarNode from PyFlow.Packages.PyFlowBase.UI.UISetVarNode import UISetVarNode from PyFlow.Packages.PyFlowBase.UI.UISequenceNode import UISequenceNode from PyFlow.Packages.PyFlowBase.UI.UICommentNode import UICommentNode -from PyFlow.Packages.PyFlowBase.UI.UIstickyNote import UIstickyNote +from PyFlow.Packages.PyFlowBase.UI.UIStickyNote import UIStickyNote from PyFlow.Packages.PyFlowBase.UI.UIRerouteNode import UIRerouteNode from PyFlow.Packages.PyFlowBase.UI.UIRerouteNodeSmall import UIRerouteNodeSmall from PyFlow.Packages.PyFlowBase.UI.UIPythonNode import UIPythonNode @@ -61,6 +62,7 @@ from PyFlow.Packages.PyFlowBase.UI.UIConvertToNode import UIConvertToNode from PyFlow.Packages.PyFlowBase.UI.UIMakeDictNode import UIMakeDictNode from PyFlow.Packages.PyFlowBase.UI.UIForLoopBeginNode import UIForLoopBeginNode +from PyFlow.Packages.PyFlowBase.UI.UIWhileLoopBeginNode import UIWhileLoopBeginNode from PyFlow.UI.Canvas.UINodeBase import UINodeBase @@ -77,7 +79,7 @@ def createUINode(raw_instance): if isinstance(raw_instance, commentNode): return UICommentNode(raw_instance) if isinstance(raw_instance, stickyNote): - return UIstickyNote(raw_instance) + return UIStickyNote(raw_instance) if isinstance(raw_instance, reroute) or isinstance(raw_instance, rerouteExecs): return UIRerouteNodeSmall(raw_instance) if isinstance(raw_instance, graphInputs): @@ -101,7 +103,9 @@ def createUINode(raw_instance): if isinstance(raw_instance, colorRamp): return UIColorRamp(raw_instance) if isinstance(raw_instance, imageDisplay): - return UIQimageDisplay(raw_instance) + return UIImageDisplayNode(raw_instance) if isinstance(raw_instance,forLoopBegin): return UIForLoopBeginNode(raw_instance) + if isinstance(raw_instance,whileLoopBegin): + return UIWhileLoopBeginNode(raw_instance) return UINodeBase(raw_instance) diff --git a/PyFlow/Packages/PyFlowBase/Nodes/forLoopEnd.py b/PyFlow/Packages/PyFlowBase/Nodes/forLoopEnd.py index 072b0a645..5fd487da3 100644 --- a/PyFlow/Packages/PyFlowBase/Nodes/forLoopEnd.py +++ b/PyFlow/Packages/PyFlowBase/Nodes/forLoopEnd.py @@ -20,9 +20,9 @@ from PyFlow.Packages.PyFlowBase.Nodes import FLOW_CONTROL_COLOR -class forLoopEnd(NodeBase): +class loopEnd(NodeBase): def __init__(self, name): - super(forLoopEnd, self).__init__(name) + super(loopEnd, self).__init__(name) self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute) self.loopBeginNode = self.createInputPin('Paired block', 'StringPin') self.loopBeginNode.setInputWidgetVariant("ObjectPathWIdget") @@ -62,4 +62,4 @@ def compute(self, *args, **kwargs): node.setError(err) self.setError(err) else: - self.setError("{} not found".format(self.loopBeginNode.getData())) + self.setError("Pair {} not found".format(self.loopBeginNode.getData())) diff --git a/PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py b/PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py new file mode 100644 index 000000000..5fd487da3 --- /dev/null +++ b/PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py @@ -0,0 +1,65 @@ +## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera + +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at + +## http://www.apache.org/licenses/LICENSE-2.0 + +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + + +from PyFlow.Core.Common import * +from PyFlow.Core.PathsRegistry import PathsRegistry +from PyFlow.Core.NodeBase import NodePinsSuggestionsHelper +from PyFlow.Core import NodeBase +from PyFlow.Packages.PyFlowBase.Nodes import FLOW_CONTROL_COLOR + + +class loopEnd(NodeBase): + def __init__(self, name): + super(loopEnd, self).__init__(name) + self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute) + self.loopBeginNode = self.createInputPin('Paired block', 'StringPin') + self.loopBeginNode.setInputWidgetVariant("ObjectPathWIdget") + self.completed = self.createOutputPin('Completed', 'ExecPin') + self.headerColor = FLOW_CONTROL_COLOR + + @staticmethod + def pinTypeHints(): + helper = NodePinsSuggestionsHelper() + helper.addInputDataType('ExecPin') + helper.addInputDataType('StringPin') + helper.addInputStruct(PinStructure.Single) + return helper + + @staticmethod + def category(): + return 'FlowControl' + + @staticmethod + def keywords(): + return ['iter', 'end'] + + @staticmethod + def description(): + return 'For loop end block' + + def compute(self, *args, **kwargs): + node = PathsRegistry().getEntity(self.loopBeginNode.getData()) + if node is not None: + if node.graph() == self.graph(): + if node.loopEndNode.getData() != self.path(): + self.setError("Invalid pair") + return + node.onNext() + else: + err = "block ends in different graphs" + node.setError(err) + self.setError(err) + else: + self.setError("Pair {} not found".format(self.loopBeginNode.getData())) diff --git a/PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py b/PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py new file mode 100644 index 000000000..18de7be6a --- /dev/null +++ b/PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py @@ -0,0 +1,85 @@ +## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera + +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at + +## http://www.apache.org/licenses/LICENSE-2.0 + +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + + +from PyFlow.Core import NodeBase +from PyFlow.Core.PathsRegistry import PathsRegistry +from PyFlow.Core.NodeBase import NodePinsSuggestionsHelper +from PyFlow.Core.Common import * +from PyFlow.Packages.PyFlowBase.Nodes import FLOW_CONTROL_COLOR + + +class whileLoopBegin(NodeBase): + def __init__(self, name): + super(whileLoopBegin, self).__init__(name) + self.lastCondition = False + self.inExec = self.createInputPin('inExec', 'ExecPin', None, self.compute) + self.condition = self.createInputPin('Condition', 'BoolPin') + self.loopEndNode = self.createInputPin('Paired block', 'StringPin') + self.loopEndNode.setInputWidgetVariant("ObjectPathWIdget") + + self.loopBody = self.createOutputPin('LoopBody', 'ExecPin') + self.headerColor = FLOW_CONTROL_COLOR + + @staticmethod + def pinTypeHints(): + helper = NodePinsSuggestionsHelper() + helper.addInputDataType('ExecPin') + helper.addInputDataType('BoolPin') + helper.addOutputDataType('ExecPin') + helper.addInputStruct(PinStructure.Single) + helper.addOutputStruct(PinStructure.Single) + return helper + + @staticmethod + def category(): + return 'FlowControl' + + @staticmethod + def keywords(): + return ['iter'] + + @staticmethod + def description(): + return 'While loop begin block' + + def onNext(self, *args, **kwargs): + currentCondition = self.condition.getData() + if currentCondition: + self.loopBody.call(*args, **kwargs) + if self.lastCondition is True and currentCondition is False: + endNodePath = self.loopEndNode.getData() + loopEndNode = PathsRegistry().getEntity(endNodePath) + loopEndNode.completed.call() + self.lastCondition = False + return + self.lastCondition = currentCondition + + def compute(self, *args, **kwargs): + endNodePath = self.loopEndNode.getData() + loopEndNode = PathsRegistry().getEntity(endNodePath) + if loopEndNode is not None: + if loopEndNode.loopBeginNode.getData() != self.path(): + self.setError("Invalid pair") + return + if self.graph() is not loopEndNode.graph(): + err = "block ends in different graphs" + self.setError(err) + loopEndNode.setError(err) + return + else: + self.setError("Pair {} not found".format(endNodePath)) + return + + self.onNext(*args, **kwargs) diff --git a/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py b/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py index c656c0d49..809e48914 100644 --- a/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py +++ b/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py @@ -242,23 +242,6 @@ def translate(self, x, y): super(UICommentNode, self).translate(x, y) def paint(self, painter, option, widget): - """CONVEX HULL TEST - path = [] - self.poly = None - for i in self.owningNodes: - relPos = self.mapFromScene(i.scenePos()) - relSize = QtCore.QPointF(i.getNodeWidth(),i.geometry().height()) - path.append((relPos.x(),relPos.y())) - path.append((relPos.x()+relSize.x(),relPos.y())) - path.append((relPos.x()+relSize.x(),relPos.y()+relSize.y())) - path.append((relPos.x(),relPos.y()+relSize.y())) - - if len(path) >= 3: - c = convex_hull(path) - self.poly = QtGui.QPolygonF() - for i in c: - self.poly.append(QtCore.QPointF(i[0], i[1])) - """ NodePainter.asCommentNode(self, painter, option, widget) def updateColor(self, color): diff --git a/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py b/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py index e2df0f6af..2c6b9561a 100644 --- a/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py +++ b/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py @@ -12,53 +12,23 @@ ## See the License for the specific language governing permissions and ## limitations under the License. +import uuid +from Qt import QtGui +from Qt import QtCore +from Qt.QtWidgets import * +from PyFlow.Core.Common import * from PyFlow.Core.NodeBase import NodeBase from PyFlow.UI.Utils.stylesheet import Colors from PyFlow.UI.Canvas.Painters import NodePainter from PyFlow.UI.Canvas.UINodeBase import UINodeBase -from PyFlow.Core.Common import * -from PyFlow.Core.PathsRegistry import PathsRegistry -from Qt import QtGui -from Qt import QtCore -from PyFlow.UI.Utils.ConvexHull import convex_hull -import uuid -from Qt.QtWidgets import * +from PyFlow.UI.Canvas.IConvexHullBackDrop import IConvexHullBackDrop -class backDrop(QGraphicsWidget): - def __init__(self, parent): - super(backDrop, self).__init__() - self.parent = parent - self.rect = QtCore.QRectF() - - def boundingRect(self): - try: - return QtCore.QRectF(QtCore.QPointF(self.parent.left - 5, self.parent.top + 5), QtCore.QPointF(self.parent.right + 5, self.parent.down - 5)) - except: - return QtCore.QRectF(0, 0, 0, 0) - - def paint(self, painter, option, widget): - roundRectPath = QtGui.QPainterPath() - self.parent.computeHull() - if self.parent.poly: - path = QtGui.QPainterPath() - path.addPolygon(self.parent.poly) - color = QtGui.QColor(self.parent.headColorOverride) - color.setAlpha(50) - pen = QtGui.QPen(self.parent.headColorOverride, 0.5) - painter.setPen(pen) - painter.fillPath(path, color) - painter.drawPath(path) - - -class UIForLoopBeginNode(UINodeBase): +class UIForLoopBeginNode(UINodeBase, IConvexHullBackDrop): def __init__(self, raw_node): super(UIForLoopBeginNode, self).__init__(raw_node) + IConvexHullBackDrop.__init__(self) self.headColorOverride = Colors.Orange - self.poly = None - self.owningLoopBeginNode = self - self.owningNodes = [] - self.backDrop = backDrop(self) def postCreate(self, jsonTemplate=None): super(UIForLoopBeginNode, self).postCreate(jsonTemplate) @@ -67,11 +37,12 @@ def postCreate(self, jsonTemplate=None): self.backDrop.update() def eventDropOnCanvas(self): + # TODO: try to simplify this with Canvas.spawnNode nodeTemplate = NodeBase.jsonTemplate() nodeTemplate['package'] = "PyFlowBase" nodeTemplate['lib'] = "" - nodeTemplate['type'] = "forLoopEnd" - nodeTemplate['name'] = self.canvasRef( ).graphManager.getUniqNodeName("forLoopEnd") + nodeTemplate['type'] = "loopEnd" + nodeTemplate['name'] = self.canvasRef().graphManager.getUniqNodeName("loopEnd") nodeTemplate['x'] = self.scenePos().x() + self.geometry().width() + 30 nodeTemplate['y'] = self.scenePos().y() nodeTemplate['uuid'] = str(uuid.uuid4()) @@ -80,45 +51,6 @@ def eventDropOnCanvas(self): endNode.getPinSG("Paired block").setData(self.path()) self.canvasRef().connectPins(self.getPinSG("LoopBody"), endNode.getPinSG(DEFAULT_IN_EXEC_NAME)) - def computeHull(self): - loopEndNode = PathsRegistry().getEntity(self.getPinSG("Paired block").getData()) - if loopEndNode is None: - self.poly = QtGui.QPolygonF() - return - p = [self] - if loopEndNode.__class__.__name__ == "forLoopEnd" and loopEndNode.getWrapper() is not None: - p.append(loopEndNode.getWrapper()) - else: - self.poly = QtGui.QPolygonF() - return - - p += self.getBetwenLoopNodes(self) - - path = [] - self.left = 0 - self.top = 0 - self.right = 0 - self.down = 0 - for i in p: - relPos = i.scenePos() - self.left = min(self.left, relPos.x()) - self.top = max(self.top, relPos.y()) - self.right = max(self.right, relPos.x()) - self.down = min(self.down, relPos.y()) - relSize = QtCore.QPointF(i.getNodeWidth(), i.geometry().height()) - path.append((relPos.x() - 5, relPos.y() - 5)) - path.append((relPos.x() + relSize.x() + 5, relPos.y() - 5)) - path.append((relPos.x() + relSize.x() + 5, relPos.y() + relSize.y() + 5)) - path.append((relPos.x() - 5, relPos.y() + relSize.y() + 5)) - - if len(path) >= 3: - self.convex_hull = convex_hull(path) - self.poly = QtGui.QPolygonF() - for i in self.convex_hull: - self.poly.append(QtCore.QPointF(i[0], i[1])) - self.poly.append(QtCore.QPointF( - self.convex_hull[0][0], self.convex_hull[0][1])) - def paint(self, painter, option, widget): self.computeHull() self.backDrop.update() diff --git a/PyFlow/Packages/PyFlowBase/UI/UIQimageDisplay.py b/PyFlow/Packages/PyFlowBase/UI/UIImageDisplayNode.py similarity index 89% rename from PyFlow/Packages/PyFlowBase/UI/UIQimageDisplay.py rename to PyFlow/Packages/PyFlowBase/UI/UIImageDisplayNode.py index c55e14ca9..0fad41205 100644 --- a/PyFlow/Packages/PyFlowBase/UI/UIQimageDisplay.py +++ b/PyFlow/Packages/PyFlowBase/UI/UIImageDisplayNode.py @@ -19,9 +19,9 @@ from Qt.QtWidgets import QLabel -class UIQimageDisplay(UINodeBase): +class UIImageDisplayNode(UINodeBase): def __init__(self, raw_node): - super(UIQimageDisplay, self).__init__(raw_node) + super(UIImageDisplayNode, self).__init__(raw_node) self.resizable = True self.Imagelabel = QLabel("test3") self.pixmap = QtGui.QPixmap(RESOURCES_DIR + "/wizard-cat.png") @@ -35,7 +35,7 @@ def onLoadImage(self, imagePath): def paint(self, painter, option, widget): self.updateSize() - super(UIQimageDisplay, self).paint(painter, option, widget) + super(UIImageDisplayNode, self).paint(painter, option, widget) def updateSize(self): scaledPixmap = self.pixmap.scaledToWidth( diff --git a/PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py b/PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py new file mode 100644 index 000000000..7003fe023 --- /dev/null +++ b/PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py @@ -0,0 +1,58 @@ +## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera + +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at + +## http://www.apache.org/licenses/LICENSE-2.0 + +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +import uuid +from Qt import QtGui +from Qt import QtCore +from Qt.QtWidgets import * +from PyFlow.UI.Utils.stylesheet import Colors +from PyFlow.UI.Canvas.Painters import NodePainter +from PyFlow.UI.Canvas.UINodeBase import UINodeBase +from PyFlow.UI.Canvas.IConvexHullBackDrop import IConvexHullBackDrop +from PyFlow.Core.Common import * +from PyFlow.Core.NodeBase import NodeBase + + +class UIWhileLoopBeginNode(UINodeBase, IConvexHullBackDrop): + def __init__(self, raw_node): + super(UIWhileLoopBeginNode, self).__init__(raw_node) + IConvexHullBackDrop.__init__(self) + self.headColorOverride = Colors.Orange + self.poly = None + + def postCreate(self, jsonTemplate=None): + super(UIWhileLoopBeginNode, self).postCreate(jsonTemplate) + self.scene().addItem(self.backDrop) + self.computeHull() + self.backDrop.update() + + def eventDropOnCanvas(self): + # TODO: try to simplify this with Canvas.spawnNode + nodeTemplate = NodeBase.jsonTemplate() + nodeTemplate['package'] = "PyFlowBase" + nodeTemplate['lib'] = "" + nodeTemplate['type'] = "loopEnd" + nodeTemplate['name'] = self.canvasRef().graphManager.getUniqNodeName("loopEnd") + nodeTemplate['x'] = self.scenePos().x() + self.geometry().width() + 30 + nodeTemplate['y'] = self.scenePos().y() + nodeTemplate['uuid'] = str(uuid.uuid4()) + endNode = self.canvasRef()._createNode(nodeTemplate) + self.getPinSG("Paired block").setData(str(endNode.path())) + endNode.getPinSG("Paired block").setData(self.path()) + self.canvasRef().connectPins(self.getPinSG("LoopBody"), endNode.getPinSG(DEFAULT_IN_EXEC_NAME)) + + def paint(self, painter, option, widget): + self.computeHull() + self.backDrop.update() + NodePainter.default(self, painter, option, widget) diff --git a/PyFlow/Packages/PyFlowBase/UI/UIstickyNote.py b/PyFlow/Packages/PyFlowBase/UI/UIstickyNote.py index 530958cef..66a585b91 100644 --- a/PyFlow/Packages/PyFlowBase/UI/UIstickyNote.py +++ b/PyFlow/Packages/PyFlowBase/UI/UIstickyNote.py @@ -25,9 +25,9 @@ from Qt.QtWidgets import (QGraphicsWidget, QGraphicsItem) -class UIstickyNote(UINodeBase): +class UIStickyNote(UINodeBase): def __init__(self, raw_node): - super(UIstickyNote, self).__init__(raw_node) + super(UIStickyNote, self).__init__(raw_node) self.color = QtGui.QColor(255, 255, 136) self.color.setAlpha(255) self.labelTextColor = QtGui.QColor(0, 0, 0, 255) @@ -47,14 +47,14 @@ def __init__(self, raw_node): self.updateSize() def serializationHook(self): - original = super(UIstickyNote, self).serializationHook() + original = super(UIStickyNote, self).serializationHook() original["color"] = self.color.rgba() original["textColor"] = self.labelTextColor.rgba() original["currentText"] = self.NonFormatedText return original def postCreate(self, jsonTemplate=None): - super(UIstickyNote, self).postCreate(jsonTemplate=jsonTemplate) + super(UIStickyNote, self).postCreate(jsonTemplate=jsonTemplate) if "color" in jsonTemplate["wrapper"]: self.color = QtGui.QColor.fromRgba( jsonTemplate["wrapper"]["color"]) @@ -70,14 +70,14 @@ def mouseDoubleClickEvent(self, event): self.textInput.setFlag(QGraphicsWidget.ItemIsFocusable, True) self.textInput.setFocus() self.startEditing() - super(UIstickyNote, self).mouseDoubleClickEvent(event) + super(UIStickyNote, self).mouseDoubleClickEvent(event) def itemChange(self, change, value): if change == QGraphicsItem.ItemSelectedChange: if value == False: self.textInput.clearFocus() self.textInput.setFlag(QGraphicsWidget.ItemIsFocusable, False) - return super(UIstickyNote, self).itemChange(change, value) + return super(UIStickyNote, self).itemChange(change, value) def aboutToCollapse(self, futureCollapseState): if not futureCollapseState: @@ -90,7 +90,7 @@ def paint(self, painter, option, widget): self.updateSize() def mouseMoveEvent(self, event): - super(UIstickyNote, self).mouseMoveEvent(event) + super(UIStickyNote, self).mouseMoveEvent(event) self.updateSize() def startEditing(self): @@ -139,7 +139,7 @@ def updateTextColor(self, color): self.update() def createPropertiesWidget(self, propertiesWidget): - super(UIstickyNote, self).createPropertiesWidget(propertiesWidget) + super(UIStickyNote, self).createPropertiesWidget(propertiesWidget) appearanceCategory = CollapsibleFormWidget(headName="Appearance") pb = pyf_ColorSlider(type="int", alpha=True, startColor=list(self.color.getRgbF())) diff --git a/PyFlow/Packages/PyFlowBase/__init__.py b/PyFlow/Packages/PyFlowBase/__init__.py index a186308ac..517f340a3 100644 --- a/PyFlow/Packages/PyFlowBase/__init__.py +++ b/PyFlow/Packages/PyFlowBase/__init__.py @@ -35,7 +35,8 @@ from PyFlow.Packages.PyFlowBase.Nodes.flipFlop import flipFlop from PyFlow.Packages.PyFlowBase.Nodes.forLoop import forLoop from PyFlow.Packages.PyFlowBase.Nodes.forLoopBegin import forLoopBegin -from PyFlow.Packages.PyFlowBase.Nodes.forLoopEnd import forLoopEnd +from PyFlow.Packages.PyFlowBase.Nodes.loopEnd import loopEnd +from PyFlow.Packages.PyFlowBase.Nodes.whileLoopBegin import whileLoopBegin from PyFlow.Packages.PyFlowBase.Nodes.forEachLoop import forEachLoop from PyFlow.Packages.PyFlowBase.Nodes.forLoopWithBreak import forLoopWithBreak from PyFlow.Packages.PyFlowBase.Nodes.retriggerableDelay import retriggerableDelay @@ -121,13 +122,14 @@ flipFlop.__name__: flipFlop, forLoop.__name__: forLoop, forLoopBegin.__name__: forLoopBegin, - forLoopEnd.__name__: forLoopEnd, + loopEnd.__name__: loopEnd, forLoopWithBreak.__name__: forLoopWithBreak, retriggerableDelay.__name__: retriggerableDelay, sequence.__name__: sequence, switchOnString.__name__: switchOnString, timer.__name__: timer, whileLoop.__name__: whileLoop, + whileLoopBegin.__name__: whileLoopBegin, commentNode.__name__: commentNode, stickyNote.__name__: stickyNote, getVar.__name__: getVar, diff --git a/PyFlow/UI/Canvas/IConvexHullBackDrop.py b/PyFlow/UI/Canvas/IConvexHullBackDrop.py new file mode 100644 index 000000000..b79e55ad3 --- /dev/null +++ b/PyFlow/UI/Canvas/IConvexHullBackDrop.py @@ -0,0 +1,76 @@ +## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera + +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at + +## http://www.apache.org/licenses/LICENSE-2.0 + +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + + +from Qt import QtGui +from Qt import QtCore +from PyFlow.UI.Canvas.loopBackDrop import backDrop +from PyFlow.Core.PathsRegistry import PathsRegistry +from PyFlow.UI.Utils.ConvexHull import convex_hull + + +class IConvexHullBackDrop(object): + """Convex hull backdrop routines. Used by for loop and while loop nodes""" + + def __init__(self): + super(IConvexHullBackDrop, self).__init__() + self.poly = None + self.left = 0 + self.top = 0 + self.right = 0 + self.down = 0 + self.convex_hull = [] + self.backDrop = backDrop(self) + + def computeHull(self): + + loopEndNodePath = self.getPinSG("Paired block").getData() + loopEndNode = PathsRegistry().getEntity(loopEndNodePath) + + if loopEndNode is None: + self.poly = QtGui.QPolygonF() + return + p = [self] + if loopEndNode.__class__.__name__ == "loopEnd" and loopEndNode.getWrapper() is not None: + p.append(loopEndNode.getWrapper()) + else: + self.poly = QtGui.QPolygonF() + return + + p += self.getBetwenLoopNodes(self) + + path = [] + self.left = 0 + self.top = 0 + self.right = 0 + self.down = 0 + for i in p: + relPos = i.scenePos() + self.left = min(self.left, relPos.x()) + self.top = max(self.top, relPos.y()) + self.right = max(self.right, relPos.x()) + self.down = min(self.down, relPos.y()) + relSize = QtCore.QPointF(i.getNodeWidth(), i.geometry().height()) + path.append((relPos.x() - 5, relPos.y() - 5)) + path.append((relPos.x() + relSize.x() + 5, relPos.y() - 5)) + path.append((relPos.x() + relSize.x() + 5, relPos.y() + relSize.y() + 5)) + path.append((relPos.x() - 5, relPos.y() + relSize.y() + 5)) + + if len(path) >= 3: + self.convex_hull = convex_hull(path) + self.poly = QtGui.QPolygonF() + for i in self.convex_hull: + self.poly.append(QtCore.QPointF(i[0], i[1])) + self.poly.append(QtCore.QPointF( + self.convex_hull[0][0], self.convex_hull[0][1])) diff --git a/PyFlow/UI/Canvas/Painters.py b/PyFlow/UI/Canvas/Painters.py index 103b9dd6b..a689d2e86 100644 --- a/PyFlow/UI/Canvas/Painters.py +++ b/PyFlow/UI/Canvas/Painters.py @@ -101,13 +101,6 @@ def asCommentNode(node, painter, option, widget): frame.right() - 5, node.labelHeight) NodePainter.drawResizeHandles(node, painter, option, widget) - """CONVEX HULL TEST - if node.poly: - path = QtGui.QPainterPath() - path.addPolygon(node.poly) - path.setFillRule(QtCore.Qt.WindingFill) - painter.fillPath(path, QtCore.Qt.red) - """ @staticmethod def drawGroups(node, painter, option, widget): inputsOffset = QtCore.QPointF(-2, 0) diff --git a/PyFlow/UI/Canvas/UINodeBase.py b/PyFlow/UI/Canvas/UINodeBase.py index 3254c400c..a0b36c966 100644 --- a/PyFlow/UI/Canvas/UINodeBase.py +++ b/PyFlow/UI/Canvas/UINodeBase.py @@ -1354,7 +1354,7 @@ def getBetwenLoopNodes(self, orig): for pin in self.UIoutputs.values(): for connection in pin.connections: node = connection.destination().topLevelItem() # topLevelItem - if node._rawNode.__class__.__name__ != "forLoopEnd": + if node._rawNode.__class__.__name__ != "loopEnd": nodes.append(node) nodes += node.getBetwenLoopNodes(orig) else: diff --git a/PyFlow/UI/Canvas/loopBackDrop.py b/PyFlow/UI/Canvas/loopBackDrop.py new file mode 100644 index 000000000..330815352 --- /dev/null +++ b/PyFlow/UI/Canvas/loopBackDrop.py @@ -0,0 +1,50 @@ +## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera + +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at + +## http://www.apache.org/licenses/LICENSE-2.0 + +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +from Qt import QtGui +from Qt import QtCore +from Qt.QtWidgets import QGraphicsWidget + + +class backDrop(QGraphicsWidget): + def __init__(self, parent): + super(backDrop, self).__init__() + self.parent = parent + self.rect = QtCore.QRectF() + self.parent._rawNode.killed.connect(self.parentNodeKilled) + + def parentNodeKilled(self, *args): + scene = self.scene() + if scene is not None: + scene.removeItem(self) + del self + + def boundingRect(self): + try: + return QtCore.QRectF(QtCore.QPointF(self.parent.left - 5, self.parent.top + 5), QtCore.QPointF(self.parent.right + 5, self.parent.down - 5)) + except: + return QtCore.QRectF(0, 0, 0, 0) + + def paint(self, painter, option, widget): + roundRectPath = QtGui.QPainterPath() + self.parent.computeHull() + if self.parent.poly: + path = QtGui.QPainterPath() + path.addPolygon(self.parent.poly) + color = QtGui.QColor(self.parent.headColorOverride) + color.setAlpha(50) + pen = QtGui.QPen(self.parent.headColorOverride, 0.5) + painter.setPen(pen) + painter.fillPath(path, color) + painter.drawPath(path) diff --git a/docs/source/PyFlow.Packages.PyFlowBase.UI.rst b/docs/source/PyFlow.Packages.PyFlowBase.UI.rst index 5f3cf3a9c..a4cfc9e76 100644 --- a/docs/source/PyFlow.Packages.PyFlowBase.UI.rst +++ b/docs/source/PyFlow.Packages.PyFlowBase.UI.rst @@ -88,10 +88,10 @@ PyFlow.Packages.PyFlowBase.UI.UIPythonNode module :members: :show-inheritance: -PyFlow.Packages.PyFlowBase.UI.UIQimageDisplay module +PyFlow.Packages.PyFlowBase.UI.UIImageDisplayNode module ---------------------------------------------------- -.. automodule:: PyFlow.Packages.PyFlowBase.UI.UIQimageDisplay +.. automodule:: PyFlow.Packages.PyFlowBase.UI.UIImageDisplayNode :members: :show-inheritance: @@ -130,10 +130,10 @@ PyFlow.Packages.PyFlowBase.UI.UISwitchOnStringNode module :members: :show-inheritance: -PyFlow.Packages.PyFlowBase.UI.UIstickyNote module +PyFlow.Packages.PyFlowBase.UI.UIStickyNote module ------------------------------------------------- -.. automodule:: PyFlow.Packages.PyFlowBase.UI.UIstickyNote +.. automodule:: PyFlow.Packages.PyFlowBase.UI.UIStickyNote :members: :show-inheritance: