def dragFinish(self, event=None): """ Stops dragging """ dragDrop(self, self.cb.getSelectionObjectSet()) # If auto force transfer: apply it after dragging to push things apart if(self.isAutoForceTransferEnabled): ForceTransfer.applyLayout() modelChange(self) # Model changed, update statusbar & undo
def dragFinish(self, event=None): """ Stops dragging """ dragDrop(self, self.cb.getSelectionObjectSet()) # If auto force transfer: apply it after dragging to push things apart if (self.isAutoForceTransferEnabled): ForceTransfer.applyLayout() modelChange(self) # Model changed, update statusbar & undo
def pasteObjectAttributes(self, event): """ Pastes attributes to the entity or link under the cursor """ itemTuple = self.cb.getItemUnderCursor(self, event) if(not itemTuple): return obj = itemTuple[2] for nodetype in self.ASGroot.nodeTypes: # iterate on all the node types... for node in self.ASGroot.listNodes[nodetype]: if(node.graphObject_ == obj): self.cb.pasteCompatibleAttributes(node, nodetype) modelChange(self) # Model changed, update statusbar & undo return
def pasteObjectAttributes(self, event): """ Pastes attributes to the entity or link under the cursor """ itemTuple = self.cb.getItemUnderCursor(self, event) if (not itemTuple): return obj = itemTuple[2] for nodetype in self.ASGroot.nodeTypes: # iterate on all the node types... for node in self.ASGroot.listNodes[nodetype]: if (node.graphObject_ == obj): self.cb.pasteCompatibleAttributes(node, nodetype) modelChange(self) # Model changed, update statusbar & undo return
def scaleReset(self, textMode = False): """ Restores scale to 1.0 """ # Re-Size the text of an Entity or Link if(textMode): allScalableEntityObjects = [] for object in self.cb.getSelectionObjectSet(): if(isEntityNode(object)): allScalableEntityObjects.append(object) elif(object.getCenterObject()): allScalableEntityObjects.append(object.getCenterObject()) for object in allScalableEntityObjects: # Get the last scale factor if(not object.layConstraints.has_key('Text Scale')): object.layConstraints['Text Scale'] = 1.00 object.ScaleText(1.00) object.layConstraints['Text Scale'] = 1.00 # Re-Size an entity node - NO QOCA elif(isNotUsingQoca()): for object in self.cb.getSelectionObjectSet(): if(isEntityNode(object)): # Get the last scale factor if(not object.layConstraints.has_key('scale')): object.layConstraints['scale'] = [1.00, 1.00] sx, sy = object.layConstraints['scale'] newSize = [1.00, 1.00] # The final scale factor sx, sy = [ newSize[0] / sx, newSize[1] / sy ] # Note: moveLinks is fals only because optimizeConnectionPorts is better #object.Scale(sx, sy, moveLinks = False) x0, y0 = object.getbbox()[:2] object.dc.scale(object.tag, x0, y0, sx, sy) object.moveTo(object.x, object.y) object.layConstraints['scale'] = newSize optimizeConnectionPorts(self) # Re-Size an entity node - WITH QOCA constraints else: allScalableEntityObjects = [] for object in self.cb.getSelectionObjectSet(): if(isEntityNode(object)): allScalableEntityObjects.append(object) # Make the width/height editable editVarList = [] for obj in allScalableEntityObjects: editVarList.append(obj.qcW) editVarList.append(obj.qcH) self.qocaSolver.addEditVars(editVarList) # Suggest new width/height values for obj in allScalableEntityObjects: self.qocaSolver.suggestVarValue([(obj.qcW, obj.sizeX), (obj.qcH, obj.sizeY)]) # Solve to get the new width/height of the entity self.qocaSolver.resolve(forceSolve=True) self.qocaSolver.endEdit() optimizeConnectionPorts(self) modelChange(self) # Model changed, update statusbar & undo
def scaleWithMotion(self, event, textMode = False): """ Scales selected entities with the mouse motion """ cb = self.cb x0, y0 = cb.getLastClickCoord() x1, y1 = cb.getCanvasCoords(event) allScalableEntityObjects = [] for object in cb.getSelectionObjectSet(): if(isEntityNode(object)): allScalableEntityObjects.append(object) elif(object.getCenterObject()): allScalableEntityObjects.append(object.getCenterObject()) # Re-Size the text of an Entity or Link if(textMode): dx, dy = [ float(x1-x0) / 100.0 , float(y1-y0) / 100.0 ] for object in allScalableEntityObjects: # Get the last scale factor, and add the movement delta if(not object.layConstraints.has_key('Text Scale')): object.layConstraints['Text Scale'] = 1.00 newSize = object.layConstraints['Text Scale'] + dy # Bigger is better, too small is unreadable :D if(newSize < 0.2): newSize = 0.2 object.ScaleText(newSize) object.layConstraints['Text Scale'] = newSize # Re-Size an entity node - NO QOCA elif(isNotUsingQoca()): dx, dy = [ float(x1-x0) / 100.0 , float(y1-y0) / 100.0 ] for object in allScalableEntityObjects: # Get the last scale factor, and add the movement delta if(not object.layConstraints.has_key('scale')): object.layConstraints['scale'] = [1.00, 1.00] sx, sy = object.layConstraints['scale'] newSize = [sx + dx, sy + dy] # Too small is not good :p if(newSize[0] < 0.2): newSize[0] = 0.2 if(newSize[1] < 0.2): newSize[1] = 0.2 # The final scale factor sx, sy = [ newSize[0] / sx, newSize[1] / sy ] # Note: moveLinks is false only because optimizeConnectionPorts is better #object.Scale(sx, sy, moveLinks = False) x0, y0 = object.getbbox()[:2] object.dc.scale(object.tag, x0, y0, sx, sy) object.moveTo(object.x, object.y) object.layConstraints['scale'] = newSize optimizeConnectionPorts(self) # Re-Size an entity node - WITH QOCA constraints else: dx, dy = [float(x1-x0), float(y1-y0) ] # Make the width/height editable editVarList = [] for obj in allScalableEntityObjects: editVarList.append(obj.qcW) editVarList.append(obj.qcH) self.qocaSolver.addEditVars(editVarList) # Suggest new width/height values for obj in allScalableEntityObjects: self.qocaSolver.suggestVarValue([(obj.qcW, obj.qcW.get() + dx), (obj.qcH, obj.qcH.get() + dy)]) # Solve to get the new width/height of the entity self.qocaSolver.resolve(forceSolve=True) self.qocaSolver.endEdit() object.layConstraints['scale'] = [obj.qcW.get() / obj.sizeX, obj.qcH.get() / obj.sizeY] optimizeConnectionPorts(self) modelChange(self) # Model changed, update statusbar & undo
def dropArrowPoints(self, event, snap = True, filterLinkTypeList=None): """ Drops intermediate points in the arrow or connects the arrow to the final object. If connection is not possible due to the final object not having connectors, the arrow will be rolled back 1 connection point, or destroyed """ cb = self.cb # User-interface callback object dc = cb.getCanvas() x, y = cb.getCanvasCoords(event) # Event coordinates arrow = self.pilotArrow.getArrow() coords = dc.coords(arrow) # Find the closest item to the arrow itemTuple = cb.getItemUnderCursor(self, event, ignore=arrow, allTags=True) if(itemTuple and snap): itemHandler, tags, obj = itemTuple # Embedded model ---> WARNING: Not tested, original AToM3 code if(len(tags) >= 2 and tags[1][:4] == 'ASG_'): self.toClass = tags[0] # Open to select the entity inside the model ATOM3TypeDialog(self, obj.semanticObject, ATOM3TypeDialog.OPEN, (None, self.setConnectMode)) # Check if we have both sides... if self.sem_objFrom and self.sem_objTo: # we have both sides... drawConnection(self) # Object elif(tags[0][:3] == 'Obj'): # Get the actual connector point that we are going to connect to # coords[-4],coords[-3] are the before last point, so the min distance # that point to the connector yields the optimal final point. # Some objects may not have connectors, in this case gracefully rollback # the arrow if(obj.hasConnectors()): coords[-2], coords[-1] = obj.getMinDistance_Connectors2Fixed(obj, coords[-4], coords[-3]) if(obj.hasNamedPorts() and self.showNamedPortMessage): name = obj.guesstimateNamedConnAtPoint(coords[-2], coords[-1]) if(name): text = 'Arrow ended on named port: ' \ + name \ + '\n\nThis message is shown only when you end an arrow' \ + ' directly on a named port' \ + '\n\nIf you choose the 2nd option and want to see these '\ + 'messages again, restart AToM3' dialog = Dialog.Dialog(None, {'title': 'Lock Current Named Port', 'text': text, 'bitmap': '', 'default': 0, 'strings': (name, 'Rollback arrow', 'Never show this message')}) if(dialog.num == 1): return if(dialog.num == 2): self.showNamedPortMessage = False # Edge just happens to have a connector elif(obj.hasCenterObjectConnector()): coords[-2], coords[-1] = obj.getCenterCoord() # Hyperedge! This means that links can connect to links elif(isHyperEdge(obj)): coords[-2], coords[-1] = obj.getCenterCoord() else: # Failure! The object had no connectors. Oooops. if(self.pilotArrow.rollbackArrow([x, y])): # self.UI_scope.event("Reset") self.UI_scope("Reset", None) return # Get the semantic object self.sem_objTo = obj.semanticObject self.sem_objFrom = self.pilotArrow.getFromObject().semanticObject self.toClass = tags[0] self.fromClass = self.pilotArrow.getFromTag() # Arrow is connecting the object to itself with just 2 points! BAD if(self.sem_objTo == self.sem_objFrom and len(coords) <= 4): pass # Draw the connection, turn off simpleConnect, since using intermediate # points else: smooth = self.pilotArrow.getSmoothness() self.inter_connect_points = coords drawConnection(self, smooth, simpleConnect = False, filterLinkTypeList=filterLinkTypeList) # Unknown something... else: raise Exception, tags # Empty variables self.fromClass = None self.toClass = None self.sem_objFrom = None self.sem_objTo = None # Model changed! modelChange(self) # Pilot arrow is no longer needed, GOODBYE! self.pilotArrow.removeArrow(False) self.UI_scope("<Arrow Created>", None) # Tell UI we succeeded! # Nope, keep adding intermediate points else: # Snap Grid if(self.snapGridInfoTuple and self.snapGridInfoTuple[2]): gridSize = self.snapGridInfoTuple[0] coords[-2:] = [ snapIt(x, x, gridSize), snapIt(y, y, gridSize) ] # No grid snapping else: coords[-2:] = [ x, y ] # Apply coords & add a duplicate point dc.coords(* [arrow] + coords + coords[-2:])
def getSelectedItemsForDelete(self, entityOnlyFlag=False): """ Use: Finds all the selected nodes and links, then creates a custom event, DeleteEvent, which it sends to the UI_scope that then sends the event to whichever scoped statechart will handle it. The DeleteEvent uses the x, y coordinates of the node/link to be deleted, so potentially, each node/link's deletion could be handled by a different scoped statechart. Parameters: self, ATOM3 instance entityOnlyFlag, if this boolean flag is True, then deleting an entity will NOT automatically delete arrows that connect to that entity """ class DeleteEvent: """ A special event that mimics the x, y coordinates of a Tkinter binding generated event, but lacks many of the normal attributes, and includes tag and itemHandler attributes not normally found there... """ def __init__(self, obj, tag=None, itemHandlerTagList=None, entityOnlyFlag=False): self.x = obj.x self.y = obj.y self.tag = tag self.semanticObject = obj.semanticObject self.atom3i = obj.semanticObject.parent self.itemHandlerTagList = itemHandlerTagList self.entityOnlyFlag = entityOnlyFlag def destroy(self): """ Deletes the associated ATOM3 node or link """ if(self.itemHandlerTagList): tag2objMap = self.atom3i.cb.getTag2ObjMap() for itemHandler, tag in self.itemHandlerTagList: # Deleting one segment of an arrow, can delete the other, so careful! if(tag2objMap.has_key(tag)): self.atom3i.deleteConnection(itemHandler, tag) else: self.atom3i.deleteRealEntity(self.tag, entityOnly=self.entityOnlyFlag) def getSemanticObject(self): """ Returns the ASGNode instance being deleted """ return self.semanticObject selectionDict = self.cb.getSelectionDict() # Dict of selected items objTagDict = dict() self.fromClass = None self.toClass = None # This little dictionary gymnastic is being used to prevent duplicate objects for tag in selectionDict: obj = selectionDict[tag][1] objTagDict.update({obj:tag}) # Now do the connections first, since if entities disappear, # connections might too. And we DO NOT want that to happen... crash crash :p for obj in objTagDict: tag = objTagDict[obj] # Connection if(isConnectionLink(obj)): #print "Now deleting connection: ", tag, obj, selectionDict[tag][0] # This may seem like overkill, but it's necessary for hyper-edges connList = [] for connTuple in obj.in_connections_: connList.append(connTuple) for connTuple in obj.out_connections_: connList.append(connTuple) itemHandlerTagList = [] for connTuple in connList: itemHandlerTagList.append(connTuple[:2]) event = DeleteEvent(obj, itemHandlerTagList=itemHandlerTagList) self.UI_scope('<serviceLinkDeleteRequest>', event) # Entity else: event = DeleteEvent(obj, tag=tag, entityOnlyFlag=entityOnlyFlag) self.UI_scope('<serviceNodeDeleteRequest>', event) # Model changed! modelChange(self) # No more selected objects :-( self.cb.clearSelectionDict()
def scaleReset(self, textMode=False): """ Restores scale to 1.0 """ # Re-Size the text of an Entity or Link if (textMode): allScalableEntityObjects = [] for object in self.cb.getSelectionObjectSet(): if (isEntityNode(object)): allScalableEntityObjects.append(object) elif (object.getCenterObject()): allScalableEntityObjects.append(object.getCenterObject()) for object in allScalableEntityObjects: # Get the last scale factor if (not object.layConstraints.has_key('Text Scale')): object.layConstraints['Text Scale'] = 1.00 object.ScaleText(1.00) object.layConstraints['Text Scale'] = 1.00 # Re-Size an entity node - NO QOCA elif (isNotUsingQoca()): for object in self.cb.getSelectionObjectSet(): if (isEntityNode(object)): # Get the last scale factor if (not object.layConstraints.has_key('scale')): object.layConstraints['scale'] = [1.00, 1.00] sx, sy = object.layConstraints['scale'] newSize = [1.00, 1.00] # The final scale factor sx, sy = [newSize[0] / sx, newSize[1] / sy] # Note: moveLinks is fals only because optimizeConnectionPorts is better #object.Scale(sx, sy, moveLinks = False) x0, y0 = object.getbbox()[:2] object.dc.scale(object.tag, x0, y0, sx, sy) object.moveTo(object.x, object.y) object.layConstraints['scale'] = newSize optimizeConnectionPorts(self) # Re-Size an entity node - WITH QOCA constraints else: allScalableEntityObjects = [] for object in self.cb.getSelectionObjectSet(): if (isEntityNode(object)): allScalableEntityObjects.append(object) # Make the width/height editable editVarList = [] for obj in allScalableEntityObjects: editVarList.append(obj.qcW) editVarList.append(obj.qcH) self.qocaSolver.addEditVars(editVarList) # Suggest new width/height values for obj in allScalableEntityObjects: self.qocaSolver.suggestVarValue([(obj.qcW, obj.sizeX), (obj.qcH, obj.sizeY)]) # Solve to get the new width/height of the entity self.qocaSolver.resolve(forceSolve=True) self.qocaSolver.endEdit() optimizeConnectionPorts(self) modelChange(self) # Model changed, update statusbar & undo
def scaleWithMotion(self, event, textMode=False): """ Scales selected entities with the mouse motion """ cb = self.cb x0, y0 = cb.getLastClickCoord() x1, y1 = cb.getCanvasCoords(event) allScalableEntityObjects = [] for object in cb.getSelectionObjectSet(): if (isEntityNode(object)): allScalableEntityObjects.append(object) elif (object.getCenterObject()): allScalableEntityObjects.append(object.getCenterObject()) # Re-Size the text of an Entity or Link if (textMode): dx, dy = [float(x1 - x0) / 100.0, float(y1 - y0) / 100.0] for object in allScalableEntityObjects: # Get the last scale factor, and add the movement delta if (not object.layConstraints.has_key('Text Scale')): object.layConstraints['Text Scale'] = 1.00 newSize = object.layConstraints['Text Scale'] + dy # Bigger is better, too small is unreadable :D if (newSize < 0.2): newSize = 0.2 object.ScaleText(newSize) object.layConstraints['Text Scale'] = newSize # Re-Size an entity node - NO QOCA elif (isNotUsingQoca()): dx, dy = [float(x1 - x0) / 100.0, float(y1 - y0) / 100.0] for object in allScalableEntityObjects: # Get the last scale factor, and add the movement delta if (not object.layConstraints.has_key('scale')): object.layConstraints['scale'] = [1.00, 1.00] sx, sy = object.layConstraints['scale'] newSize = [sx + dx, sy + dy] # Too small is not good :p if (newSize[0] < 0.2): newSize[0] = 0.2 if (newSize[1] < 0.2): newSize[1] = 0.2 # The final scale factor sx, sy = [newSize[0] / sx, newSize[1] / sy] # Note: moveLinks is false only because optimizeConnectionPorts is better #object.Scale(sx, sy, moveLinks = False) x0, y0 = object.getbbox()[:2] object.dc.scale(object.tag, x0, y0, sx, sy) object.moveTo(object.x, object.y) object.layConstraints['scale'] = newSize optimizeConnectionPorts(self) # Re-Size an entity node - WITH QOCA constraints else: dx, dy = [float(x1 - x0), float(y1 - y0)] # Make the width/height editable editVarList = [] for obj in allScalableEntityObjects: editVarList.append(obj.qcW) editVarList.append(obj.qcH) self.qocaSolver.addEditVars(editVarList) # Suggest new width/height values for obj in allScalableEntityObjects: self.qocaSolver.suggestVarValue([(obj.qcW, obj.qcW.get() + dx), (obj.qcH, obj.qcH.get() + dy)]) # Solve to get the new width/height of the entity self.qocaSolver.resolve(forceSolve=True) self.qocaSolver.endEdit() object.layConstraints['scale'] = [ obj.qcW.get() / obj.sizeX, obj.qcH.get() / obj.sizeY ] optimizeConnectionPorts(self) modelChange(self) # Model changed, update statusbar & undo
def dropArrowPoints(self, event, snap=True, filterLinkTypeList=None): """ Drops intermediate points in the arrow or connects the arrow to the final object. If connection is not possible due to the final object not having connectors, the arrow will be rolled back 1 connection point, or destroyed """ cb = self.cb # User-interface callback object dc = cb.getCanvas() x, y = cb.getCanvasCoords(event) # Event coordinates arrow = self.pilotArrow.getArrow() coords = dc.coords(arrow) # Find the closest item to the arrow itemTuple = cb.getItemUnderCursor(self, event, ignore=arrow, allTags=True) if (itemTuple and snap): itemHandler, tags, obj = itemTuple # Embedded model ---> WARNING: Not tested, original AToM3 code if (len(tags) >= 2 and tags[1][:4] == 'ASG_'): self.toClass = tags[0] # Open to select the entity inside the model ATOM3TypeDialog(self, obj.semanticObject, ATOM3TypeDialog.OPEN, (None, self.setConnectMode)) # Check if we have both sides... if self.sem_objFrom and self.sem_objTo: # we have both sides... drawConnection(self) # Object elif (tags[0][:3] == 'Obj'): # Get the actual connector point that we are going to connect to # coords[-4],coords[-3] are the before last point, so the min distance # that point to the connector yields the optimal final point. # Some objects may not have connectors, in this case gracefully rollback # the arrow if (obj.hasConnectors()): coords[-2], coords[-1] = obj.getMinDistance_Connectors2Fixed( obj, coords[-4], coords[-3]) if (obj.hasNamedPorts() and self.showNamedPortMessage): name = obj.guesstimateNamedConnAtPoint( coords[-2], coords[-1]) if (name): text = 'Arrow ended on named port: ' \ + name \ + '\n\nThis message is shown only when you end an arrow' \ + ' directly on a named port' \ + '\n\nIf you choose the 2nd option and want to see these '\ + 'messages again, restart AToM3' dialog = Dialog.Dialog( None, { 'title': 'Lock Current Named Port', 'text': text, 'bitmap': '', 'default': 0, 'strings': (name, 'Rollback arrow', 'Never show this message') }) if (dialog.num == 1): return if (dialog.num == 2): self.showNamedPortMessage = False # Edge just happens to have a connector elif (obj.hasCenterObjectConnector()): coords[-2], coords[-1] = obj.getCenterCoord() # Hyperedge! This means that links can connect to links elif (isHyperEdge(obj)): coords[-2], coords[-1] = obj.getCenterCoord() else: # Failure! The object had no connectors. Oooops. if (self.pilotArrow.rollbackArrow([x, y])): # self.UI_scope.event("Reset") self.UI_scope("Reset", None) return # Get the semantic object self.sem_objTo = obj.semanticObject self.sem_objFrom = self.pilotArrow.getFromObject().semanticObject self.toClass = tags[0] self.fromClass = self.pilotArrow.getFromTag() # Arrow is connecting the object to itself with just 2 points! BAD if (self.sem_objTo == self.sem_objFrom and len(coords) <= 4): pass # Draw the connection, turn off simpleConnect, since using intermediate # points else: smooth = self.pilotArrow.getSmoothness() self.inter_connect_points = coords drawConnection(self, smooth, simpleConnect=False, filterLinkTypeList=filterLinkTypeList) # Unknown something... else: raise Exception, tags # Empty variables self.fromClass = None self.toClass = None self.sem_objFrom = None self.sem_objTo = None # Model changed! modelChange(self) # Pilot arrow is no longer needed, GOODBYE! self.pilotArrow.removeArrow(False) self.UI_scope("<Arrow Created>", None) # Tell UI we succeeded! # Nope, keep adding intermediate points else: # Snap Grid if (self.snapGridInfoTuple and self.snapGridInfoTuple[2]): gridSize = self.snapGridInfoTuple[0] coords[-2:] = [snapIt(x, x, gridSize), snapIt(y, y, gridSize)] # No grid snapping else: coords[-2:] = [x, y] # Apply coords & add a duplicate point dc.coords(*[arrow] + coords + coords[-2:])
def getSelectedItemsForDelete(self, entityOnlyFlag=False): """ Use: Finds all the selected nodes and links, then creates a custom event, DeleteEvent, which it sends to the UI_scope that then sends the event to whichever scoped statechart will handle it. The DeleteEvent uses the x, y coordinates of the node/link to be deleted, so potentially, each node/link's deletion could be handled by a different scoped statechart. Parameters: self, ATOM3 instance entityOnlyFlag, if this boolean flag is True, then deleting an entity will NOT automatically delete arrows that connect to that entity """ class DeleteEvent: """ A special event that mimics the x, y coordinates of a Tkinter binding generated event, but lacks many of the normal attributes, and includes tag and itemHandler attributes not normally found there... """ def __init__(self, obj, tag=None, itemHandlerTagList=None, entityOnlyFlag=False): self.x = obj.x self.y = obj.y self.tag = tag self.semanticObject = obj.semanticObject self.atom3i = obj.semanticObject.parent self.itemHandlerTagList = itemHandlerTagList self.entityOnlyFlag = entityOnlyFlag def destroy(self): """ Deletes the associated ATOM3 node or link """ if (self.itemHandlerTagList): tag2objMap = self.atom3i.cb.getTag2ObjMap() for itemHandler, tag in self.itemHandlerTagList: # Deleting one segment of an arrow, can delete the other, so careful! if (tag2objMap.has_key(tag)): self.atom3i.deleteConnection(itemHandler, tag) else: self.atom3i.deleteRealEntity(self.tag, entityOnly=self.entityOnlyFlag) def getSemanticObject(self): """ Returns the ASGNode instance being deleted """ return self.semanticObject selectionDict = self.cb.getSelectionDict() # Dict of selected items objTagDict = dict() self.fromClass = None self.toClass = None # This little dictionary gymnastic is being used to prevent duplicate objects for tag in selectionDict: obj = selectionDict[tag][1] objTagDict.update({obj: tag}) # Now do the connections first, since if entities disappear, # connections might too. And we DO NOT want that to happen... crash crash :p for obj in objTagDict: tag = objTagDict[obj] # Connection if (isConnectionLink(obj)): #print "Now deleting connection: ", tag, obj, selectionDict[tag][0] # This may seem like overkill, but it's necessary for hyper-edges connList = [] for connTuple in obj.in_connections_: connList.append(connTuple) for connTuple in obj.out_connections_: connList.append(connTuple) itemHandlerTagList = [] for connTuple in connList: itemHandlerTagList.append(connTuple[:2]) event = DeleteEvent(obj, itemHandlerTagList=itemHandlerTagList) self.UI_scope('<serviceLinkDeleteRequest>', event) # Entity else: event = DeleteEvent(obj, tag=tag, entityOnlyFlag=entityOnlyFlag) self.UI_scope('<serviceNodeDeleteRequest>', event) # Model changed! modelChange(self) # No more selected objects :-( self.cb.clearSelectionDict()