def retrieveImage(self, frame, frameChanged): """ Computes the node at the frame indicated if the frame has changed (if the time has changed). """ buttleData = ButtleDataSingleton().get() #Get the name of the currentNode of the viewer node = buttleData.getCurrentViewerNodeName() #Get the gloabl hashCode of the node if node is not None: hashMap = tuttle.NodeHashContainer() buttleData.getGraph().getGraphTuttle().computeGlobalHashAtTime(hashMap, frame) node_hashCode = hashMap.getHash(node, frame) #Get the map mapNodeToImage = buttleData.getMapNodeNameToComputedImage() try: self.setNodeError("") for key in mapNodeToImage.keys(): #If the image is already calculated if node_hashCode == key and frameChanged is False: #print "**************************Image already calculated**********************" return mapNodeToImage.get(node_hashCode) #If it is not #print "**************************Image is not already calculated**********************" return self.computeNode(node, frame) except Exception as e: logging.debug("Can't display node : " + node) self.setNodeError(str(e)) raise
def duplicationNode(self): """ Duplicates the current node(s). """ buttleData = ButtleDataSingleton().get() if buttleData.getCurrentSelectedNodeWrappers() != []: for node in buttleData.getCurrentSelectedNodeWrappers(): # Create a node giving the current selected node's type, x and y nodeType = node.getNode().getType() coord = node.getNode().getCoord() buttleData.getGraph().createNode(nodeType, coord[0], coord[1]) newNode = buttleData.getGraph().getNodes()[-1] # Get the current selected node's properties nameUser = node.getNameUser() + "_duplicate" oldCoord = node.getNode().getOldCoord() color = node.getNode().getColor() # Use the current selected node's properties to set the duplicated node's properties newNode.setNameUser(nameUser) newNode.setOldCoord(oldCoord[0], oldCoord[1]) newNode.setColor(color) newNode.getTuttleNode().getParamSet().copyParamsValues(node.getNode().getTuttleNode().getParamSet()) # update undo/redo display self.undoRedoChanged()
def destructionNodes(self): """ Deletes the current node(s). """ buttleData = ButtleDataSingleton().get() # if the params of the current node deleted are display if buttleData.getCurrentParamNodeName() in buttleData.getCurrentSelectedNodeNames(): buttleData.setCurrentParamNodeName(None) # if the viewer of the current node deleted is display if buttleData.getCurrentViewerNodeName() in buttleData.getCurrentSelectedNodeNames(): buttleData.setCurrentViewerNodeName(None) # if the viewer display a node affected by the destruction # need something from Tuttle # if at least one node in the graph if len(buttleData.getGraphWrapper().getNodeWrappers()) > 0 and len(buttleData.getGraph().getNodes()) > 0: # if a node is selected if buttleData.getCurrentSelectedNodeNames() != []: buttleData.getGraph().deleteNodes([nodeWrapper.getNode() for nodeWrapper in buttleData.getCurrentSelectedNodeWrappers()]) buttleData.clearCurrentSelectedNodeNames() # emit signals buttleData.currentParamNodeChanged.emit() buttleData.currentViewerNodeChanged.emit() buttleData.currentSelectedNodesChanged.emit() # update undo/redo display self.undoRedoChanged()
def duplicationNode(self): """ Duplicates the current node(s). """ buttleData = ButtleDataSingleton().get() if buttleData.getCurrentSelectedNodeWrappers() != []: for node in buttleData.getCurrentSelectedNodeWrappers(): # Create a node giving the current selected node's type, x and y nodeType = node.getNode().getType() coord = node.getNode().getCoord() buttleData.getGraph().createNode(nodeType, coord[0], coord[1]) newNode = buttleData.getGraph().getNodes()[-1] # Get the current selected node's properties nameUser = node.getNameUser() + "_duplicate" oldCoord = node.getNode().getOldCoord() color = node.getNode().getColor() # Use the current selected node's properties to set the duplicated node's properties newNode.setNameUser(nameUser) newNode.setOldCoord(oldCoord[0], oldCoord[1]) newNode.setColor(color) newNode.getTuttleNode().getParamSet().copyParamsValues( node.getNode().getTuttleNode().getParamSet()) # update undo/redo display self.undoRedoChanged()
def retrieveImage(self, frame, frameChanged): """ Computes the node at the frame indicated if the frame has changed (if the time has changed). """ buttleData = ButtleDataSingleton().get() #Get the name of the currentNode of the viewer node = buttleData.getCurrentViewerNodeName() #Get the gloabl hashCode of the node if node is not None: hashMap = tuttle.NodeHashContainer() buttleData.getGraph().getGraphTuttle().computeGlobalHashAtTime( hashMap, frame) node_hashCode = hashMap.getHash(node, frame) #Get the map mapNodeToImage = buttleData.getMapNodeNameToComputedImage() try: self.setNodeError("") for key in mapNodeToImage.keys(): #If the image is already calculated if node_hashCode == key and frameChanged is False: #print "**************************Image already calculated**********************" return mapNodeToImage.get(node_hashCode) #If it is not #print "**************************Image is not already calculated**********************" return self.computeNode(node, frame) except Exception as e: logging.debug("Can't display node : " + node) self.setNodeError(str(e)) raise
def disconnect(self, connectionWrapper): """ Removes a connection between 2 clips. """ buttleData = ButtleDataSingleton().get() buttleData.getGraph().deleteConnection( connectionWrapper.getConnection())
def nodeMoved(self, nodeName, x, y): """ This function pushes a cmdMoved in the CommandManager. """ buttleData = ButtleDataSingleton().get() buttleData.getGraph().nodeMoved(nodeName, x, y) # update undo/redo display self.undoRedoChanged()
def creationNode(self, nodeType, x=20, y=20): """ Creates a node. """ buttleData = ButtleDataSingleton().get() buttleData.getGraph().createNode(nodeType, x, y) # update undo/redo display self.undoRedoChanged()
def getNbFrames(self): """ Returns the number of frames of this node. """ #import which needs to be changed in the future from buttleofx.data import ButtleDataSingleton buttleData = ButtleDataSingleton().get() graph = buttleData.getGraph().getGraphTuttle() node = self._node.getTuttleNode().asImageEffectNode() try: self.setFrameError("") graph.setup() except Exception as e: logging.debug("can't get nbFrames of the node" + self._node.getName()) self.setFrameError(str(e)) return 0 raise timeDomain = node.getTimeDomain( ) #getTimeDomain() returns first frame and last one nbFrames = timeDomain.max - timeDomain.min #not very elegant but allow to avoid a problem because an image returns a number of frames very high if nbFrames > 100000000 or nbFrames < 0: nbFrames = 1 #print "nbFrames: ", nbFrames return nbFrames
def dropFile(self, url, x, y): """ Drops a file on the graph. - Image or video : creates a reader node. - Json : load a graph (if the format allows it) """ buttleData = ButtleDataSingleton().get() extension = url.split(".")[-1].lower() if extension == 'bofx': buttleData.loadData(url) # also need to verify the json format else: buttleData.getGraph().createReaderNode(url, x, y) # update undo/redo display self.undoRedoChanged()
def nodeMoved(self, nodeName, newX, newY): """ This function pushes a cmdMoved in the CommandManager. """ from buttleofx.data import ButtleDataSingleton buttleData = ButtleDataSingleton().get() node = buttleData.getGraph().getNode(nodeName) # What is the value of the movement (compared to the old position) ? oldX, oldY = node.getOldCoord() xMovement = newX - oldX yMovement = newY - oldY # if the node did'nt really move, nothing is done if (xMovement, xMovement) == (0, 0): return commands = [] # we create a GroupUndoableCommands of CmdSetCoord for each selected node for selectedNodeWrapper in buttleData.getCurrentSelectedNodeWrappers(): # we get the needed informations for this node selectedNode = selectedNodeWrapper.getNode() selectedNodeName = selectedNode.getName() oldX, oldY = selectedNode.getOldCoord() # we set the new coordinates of the node (each selected node is doing the same movement) cmdMoved = CmdSetCoord(self, selectedNodeName, (oldX + xMovement, oldY + yMovement)) commands.append(cmdMoved) # then we push the group of commands CommandManager().push(GroupUndoableCommands(commands))
def canConnect(self, clip1, clip2): """ Returns True if the connection between the nodes is possible, else False. A connection is possible if the clip isn't already taken, and if the clips are from 2 different nodes, not already connected. """ buttleData = ButtleDataSingleton().get() graph = buttleData.getGraph() # if the clips are from the same node : False if (clip1.getNodeName() == clip2.getNodeName()): return False # if the clips are 2 inputs or 2 outputs : False if (clip1.getClipName() == "Output" and clip2.getClipName() == "Output") or (clip1.getClipName() != "Output" and clip2.getClipName() != "Output"): return False # if the input clip is already taken : False if (clip1.getClipName() != "Output" and graph.contains(clip1)) or (clip2.getClipName() != "Output" and graph.contains(clip2)): return False # if the nodes containing the clips are already connected : False if(graph.nodesConnected(clip2, clip1)): return False return True
def canConnect(self, clip1, clip2): """ Returns True if the connection between the nodes is possible, else False. A connection is possible if the clip isn't already taken, and if the clips are from 2 different nodes, not already connected. """ buttleData = ButtleDataSingleton().get() graph = buttleData.getGraph() # if the clips are from the same node : False if (clip1.getNodeName() == clip2.getNodeName()): return False # if the clips are 2 inputs or 2 outputs : False if (clip1.getClipName() == "Output" and clip2.getClipName() == "Output") or (clip1.getClipName() != "Output" and clip2.getClipName() != "Output"): return False # if the input clip is already taken : False if (clip1.getClipName() != "Output" and graph.contains(clip1)) or (clip2.getClipName() != "Output" and graph.contains(clip2)): return False # if the nodes containing the clips are already connected : False if (graph.nodesConnected(clip2, clip1)): return False return True
def pasteNode(self): """ Pasts the current node(s). """ buttleData = ButtleDataSingleton().get() # If nodes have been copied previously if buttleData.getCurrentCopiedNodesInfo(): # Create a copy for each node copied for node in buttleData.getCurrentCopiedNodesInfo(): buttleData.getGraph().createNode(buttleData.getCurrentCopiedNodesInfo()[node]["nodeType"], 20, 20) newNode = buttleData.getGraph().getNodes()[-1] newNode.setColor(buttleData.getCurrentCopiedNodesInfo()[node]["color"]) newNode.setNameUser(buttleData.getCurrentCopiedNodesInfo()[node]["nameUser"] + buttleData.getCurrentCopiedNodesInfo()[node]["mode"]) newNode.getTuttleNode().getParamSet().copyParamsValues(buttleData.getCurrentCopiedNodesInfo()[node]["params"]) # update undo/redo display self.undoRedoChanged()
def connectionDropEvent(self, dataTmpClip, clip, clipIndex): """ Creates or deletes a connection between 2 clips ('tmpClip', the "dragged" clip, and 'newClip', the "dropped" clip) Arguments : - dataTmpClip : the string from mimeData, identifying the tmpClip (the "dragged" clip). - clip : the ClipWrapper of the "dropped" clip. - clipIndex : the index of the "dropped" clip. """ buttleData = ButtleDataSingleton().get() # we split the data of the tmpClip (from mimeData) to find needed informations about this clip. infosTmpClip = dataTmpClip.split("/") if infosTmpClip[0] != "clip" or len(infosTmpClip) != 4: return # use exception ! else: tmpClipNodeName, tmpClipName, tmpClipIndex = infosTmpClip[ 1], infosTmpClip[2], int(infosTmpClip[3]) # we find the position of this tmpClip to be able to create a IdClip object. positionTmpClip = buttleData.getGraphWrapper().getPositionClip( tmpClipNodeName, tmpClipName, tmpClipIndex) tmpClip = IdClip(tmpClipNodeName, tmpClipName, clipIndex, positionTmpClip) if tmpClip: # idem, for the "dropped" clip = newClip positionNewClip = buttleData.getGraphWrapper().getPositionClip( clip.getNodeName(), clip.getClipName(), clipIndex) newClip = IdClip(clip.getNodeName(), clip.getClipName(), clipIndex, positionNewClip) # a connection must be created from the ouput clip to the input clip (the order of the arguments is important !) if tmpClip.getClipName() == "Output": clipOut, clipIn = tmpClip, newClip else: clipOut, clipIn = newClip, tmpClip # if the clips can be connected, we connect them if self.canConnect(clipOut, clipIn): self.connect(clipOut, clipIn) return # else if they can't be connected, we check if they are already connected, and disconnect them if it is the case. else: connection = buttleData.getGraph().getConnectionByClips( clipOut, clipIn) if connection: self.disconnect( buttleData.getGraphWrapper().getConnectionWrapper( connection.getId())) return # update undo/redo display self.undoRedoChanged()
def nodeIsMoving(self, nodeName, newX, newY): """ This function updates the position of the selected nodes and the connections, when one or several nodes are moving. """ buttleData = ButtleDataSingleton().get() node = buttleData.getGraph().getNode(nodeName) # What is the value of the movement (compared to the old position) ? oldX, oldY = node.getCoord() xMovement = newX - oldX yMovement = newY - oldY # for each selected node, we update the position considering the value of the movement for selectedNodeWrapper in buttleData.getCurrentSelectedNodeWrappers(): selectedNode = selectedNodeWrapper.getNode() currentX, currentY = selectedNode.getCoord() selectedNode.setCoord(currentX + xMovement, currentY + yMovement) # we update also the position of all the connections buttleData.getGraph().connectionsCoordChanged(selectedNode)
def pasteNode(self): """ Pasts the current node(s). """ buttleData = ButtleDataSingleton().get() # If nodes have been copied previously if buttleData.getCurrentCopiedNodesInfo(): # Create a copy for each node copied for node in buttleData.getCurrentCopiedNodesInfo(): buttleData.getGraph().createNode( buttleData.getCurrentCopiedNodesInfo()[node]["nodeType"], 20, 20) newNode = buttleData.getGraph().getNodes()[-1] newNode.setColor( buttleData.getCurrentCopiedNodesInfo()[node]["color"]) newNode.setNameUser( buttleData.getCurrentCopiedNodesInfo()[node]["nameUser"] + buttleData.getCurrentCopiedNodesInfo()[node]["mode"]) newNode.getTuttleNode().getParamSet().copyParamsValues( buttleData.getCurrentCopiedNodesInfo()[node]["params"]) # update undo/redo display self.undoRedoChanged()
def destructionNodes(self): """ Deletes the current node(s). """ buttleData = ButtleDataSingleton().get() # if the params of the current node deleted are display if buttleData.getCurrentParamNodeName( ) in buttleData.getCurrentSelectedNodeNames(): buttleData.setCurrentParamNodeName(None) # if the viewer of the current node deleted is display if buttleData.getCurrentViewerNodeName( ) in buttleData.getCurrentSelectedNodeNames(): buttleData.setCurrentViewerNodeName(None) # if the viewer display a node affected by the destruction # need something from Tuttle # if at least one node in the graph if len(buttleData.getGraphWrapper().getNodeWrappers()) > 0 and len( buttleData.getGraph().getNodes()) > 0: # if a node is selected if buttleData.getCurrentSelectedNodeNames() != []: buttleData.getGraph().deleteNodes([ nodeWrapper.getNode() for nodeWrapper in buttleData.getCurrentSelectedNodeWrappers() ]) buttleData.clearCurrentSelectedNodeNames() # emit signals buttleData.currentParamNodeChanged.emit() buttleData.currentViewerNodeChanged.emit() buttleData.currentSelectedNodesChanged.emit() # update undo/redo display self.undoRedoChanged()
def connectionDropEvent(self, dataTmpClip, clip, clipIndex): """ Creates or deletes a connection between 2 clips ('tmpClip', the "dragged" clip, and 'newClip', the "dropped" clip) Arguments : - dataTmpClip : the string from mimeData, identifying the tmpClip (the "dragged" clip). - clip : the ClipWrapper of the "dropped" clip. - clipIndex : the index of the "dropped" clip. """ buttleData = ButtleDataSingleton().get() # we split the data of the tmpClip (from mimeData) to find needed informations about this clip. infosTmpClip = dataTmpClip.split("/") if infosTmpClip[0] != "clip" or len(infosTmpClip) != 4: return # use exception ! else: tmpClipNodeName, tmpClipName, tmpClipIndex = infosTmpClip[1], infosTmpClip[2], int(infosTmpClip[3]) # we find the position of this tmpClip to be able to create a IdClip object. positionTmpClip = buttleData.getGraphWrapper().getPositionClip(tmpClipNodeName, tmpClipName, tmpClipIndex) tmpClip = IdClip(tmpClipNodeName, tmpClipName, clipIndex, positionTmpClip) if tmpClip: # idem, for the "dropped" clip = newClip positionNewClip = buttleData.getGraphWrapper().getPositionClip(clip.getNodeName(), clip.getClipName(), clipIndex) newClip = IdClip(clip.getNodeName(), clip.getClipName(), clipIndex, positionNewClip) # a connection must be created from the ouput clip to the input clip (the order of the arguments is important !) if tmpClip.getClipName() == "Output": clipOut, clipIn = tmpClip, newClip else: clipOut, clipIn = newClip, tmpClip # if the clips can be connected, we connect them if self.canConnect(clipOut, clipIn): self.connect(clipOut, clipIn) return # else if they can't be connected, we check if they are already connected, and disconnect them if it is the case. else: connection = buttleData.getGraph().getConnectionByClips(clipOut, clipIn) if connection: self.disconnect(buttleData.getGraphWrapper().getConnectionWrapper(connection.getId())) return # update undo/redo display self.undoRedoChanged()
def launchProcessGraph(self): buttleData = ButtleDataSingleton().get() #Get the name of the currentNode of the viewer node = buttleData.getCurrentViewerNodeName() # initialization of the process graph graph = buttleData.getGraph().getGraphTuttle() # timeRange between the frames of beginning and end (first frame, last frame, step) timeRange = tuttle.TimeRange(self._frame, self._nbFrames, 1) self._processOptions = tuttle.ComputeOptions(self._frame, self._nbFrames, 1) processGraph = tuttle.ProcessGraph(self._processOptions, graph, [node]) processGraph.setup() processGraph.beginSequence(timeRange) # communicate processGraph to buttleData buttleData.setProcessGraph(processGraph) buttleData.setVideoIsPlaying(True)
def computeNode(self, node, frame): """ Computes the node (displayed in the viewer) at the frame indicated. """ buttleData = ButtleDataSingleton().get() graphTuttle = buttleData.getGraph().getGraphTuttle() #Get the output where we save the result self._tuttleImageCache = tuttle.MemoryCache() if buttleData.getVideoIsPlaying(): # if a video is playing processGraph = buttleData.getProcessGraph() processGraph.setupAtTime(frame) processGraph.processAtTime(self._tuttleImageCache, frame) else: # if it's an image only processOptions = tuttle.ComputeOptions(int(frame)) processGraph = tuttle.ProcessGraph(processOptions, graphTuttle, [node]) processGraph.setup() timeRange = tuttle.TimeRange(frame, frame, 1) # buttleData.getTimeRange() processGraph.beginSequence(timeRange) processGraph.setupAtTime(frame) processGraph.processAtTime(self._tuttleImageCache, frame) processGraph.endSequence() self._computedImage = self._tuttleImageCache.get(0) #Add the computedImage to the map hashMap = tuttle.NodeHashContainer() graphTuttle.computeGlobalHashAtTime(hashMap, frame) hasCode = hashMap.getHash(node, frame) #Max 15 computedImages saved in memory if hasCode not in buttleData._mapNodeNameToComputedImage.keys( ) and len(buttleData._mapNodeNameToComputedImage) < 15: buttleData._mapNodeNameToComputedImage.update( {hasCode: self._computedImage}) elif hasCode not in buttleData._mapNodeNameToComputedImage.keys( ) and len(buttleData._mapNodeNameToComputedImage) >= 15: #Delete a computed image from the memory (random) buttleData._mapNodeNameToComputedImage.popitem() buttleData._mapNodeNameToComputedImage.update( {hasCode: self._computedImage}) return self._computedImage
def getFPS(self): """ Returns the FPS of this node. """ #import which needs to be changed in the future from buttleofx.data import ButtleDataSingleton buttleData = ButtleDataSingleton().get() graph = buttleData.getGraph().getGraphTuttle() node = self._node.getTuttleNode().asImageEffectNode() try: self.setFpsError("") graph.setup() except Exception as e: logging.debug("can't get fps of the node" + self._node.getName()) self.setFpsError(str(e)) return 1 raise framerate = node.getOutputFrameRate() #print "framerate: ", framerate return framerate
def computeNode(self, node, frame): """ Computes the node (displayed in the viewer) at the frame indicated. """ buttleData = ButtleDataSingleton().get() graphTuttle = buttleData.getGraph().getGraphTuttle() #Get the output where we save the result self._tuttleImageCache = tuttle.MemoryCache() if buttleData.getVideoIsPlaying(): # if a video is playing processGraph = buttleData.getProcessGraph() processGraph.setupAtTime(frame) processGraph.processAtTime(self._tuttleImageCache, frame) else: # if it's an image only processOptions = tuttle.ComputeOptions(int(frame)) processGraph = tuttle.ProcessGraph(processOptions, graphTuttle, [node]) processGraph.setup() timeRange = tuttle.TimeRange(frame, frame, 1) # buttleData.getTimeRange() processGraph.beginSequence(timeRange) processGraph.setupAtTime(frame) processGraph.processAtTime(self._tuttleImageCache, frame) processGraph.endSequence() self._computedImage = self._tuttleImageCache.get(0) #Add the computedImage to the map hashMap = tuttle.NodeHashContainer() graphTuttle.computeGlobalHashAtTime(hashMap, frame) hasCode = hashMap.getHash(node, frame) #Max 15 computedImages saved in memory if hasCode not in buttleData._mapNodeNameToComputedImage.keys() and len(buttleData._mapNodeNameToComputedImage) < 15: buttleData._mapNodeNameToComputedImage.update({hasCode: self._computedImage}) elif hasCode not in buttleData._mapNodeNameToComputedImage.keys() and len(buttleData._mapNodeNameToComputedImage) >= 15: #Delete a computed image from the memory (random) buttleData._mapNodeNameToComputedImage.popitem() buttleData._mapNodeNameToComputedImage.update({hasCode: self._computedImage}) return self._computedImage
def getNbFrames(self): """ Returns the number of frames of this node. """ #import which needs to be changed in the future from buttleofx.data import ButtleDataSingleton buttleData = ButtleDataSingleton().get() graph = buttleData.getGraph().getGraphTuttle() node = self._node.getTuttleNode().asImageEffectNode() try: self.setFrameError("") graph.setup() except Exception as e: logging.debug("can't get nbFrames of the node" + self._node.getName()) self.setFrameError(str(e)) return 0 raise timeDomain = node.getTimeDomain() #getTimeDomain() returns first frame and last one nbFrames = timeDomain.max - timeDomain.min #not very elegant but allow to avoid a problem because an image returns a number of frames very high if nbFrames > 100000000 or nbFrames < 0: nbFrames = 1 #print "nbFrames: ", nbFrames return nbFrames
def connect(self, clipOut, clipIn): """ Adds a connection between 2 clips. """ buttleData = ButtleDataSingleton().get() buttleData.getGraph().createConnection(clipOut, clipIn)
def disconnect(self, connectionWrapper): """ Removes a connection between 2 clips. """ buttleData = ButtleDataSingleton().get() buttleData.getGraph().deleteConnection(connectionWrapper.getConnection())