def __updateKeyFrame(self): # if multiple keys selected display "---" unless all selected keys have same value # which can only happen when all the keys have different parent curves selectedKeys = self.parent().curveGadget().selectedKeys() time = sole(key.getTime() for key in selectedKeys) if time is not None: context = selectedKeys[0].parent().ancestor( Gaffer.ScriptNode).context() frame = int(round(time * context.getFramesPerSecond())) with Gaffer.BlockedConnection(self.__frameConnection): self.__frameEditor.setValue(frame) else: with Gaffer.BlockedConnection(self.__frameConnection): self.__frameEditor.setText("") self.__frameEditor._qtWidget().setPlaceholderText("---") # set enabled when all selected keys have different parent curves enabled = bool(selectedKeys) curves = set() for key in selectedKeys: if key.parent() in curves: enabled = False break curves.add(key.parent()) self.__frameEditor.setEnabled(enabled)
def testReentrant( self ) : self.numCalls = 0 def f() : self.numCalls += 1 s = Gaffer.Signal0() c = s.connect( f ) s() self.assertEqual( self.numCalls, 1 ) with Gaffer.BlockedConnection( c ) : s() self.assertEqual( self.numCalls, 1 ) with Gaffer.BlockedConnection( c ) : s() self.assertEqual( self.numCalls, 1 ) s() self.assertEqual( self.numCalls, 2 ) with Gaffer.BlockedConnection( c ) : s() self.assertEqual( self.numCalls, 2 ) with Gaffer.BlockedConnection( c ) : s() self.assertEqual( self.numCalls, 2 ) s() self.assertEqual( self.numCalls, 3 )
def _updateFromPlug(self): self.__multiSelectionMenu.setEnabled(self._editable()) if self.getPlug() is not None: with self.getContext(): # Check to see if the format is the default instance. If so then it means we are tracking # the default format and therefore do not need to change the UI. if self.getPlug().getValue().getDisplayWindow().isEmpty( ) and not self.__isDefaultFormatPlug: self.__multiSelectionMenu.setSelection("Default Format") return # Otherwise update the UI from the plug. plugValue = self.getPlug().getValue() for name in self.__formats.keys(): format = self.__formats[name] if format == plugValue: with Gaffer.BlockedConnection( self.__currentChangedConnection): self.__multiSelectionMenu.setSelection(name) return # The format is new so we should add it to our menu... GafferImage.Format.registerFormat(plugValue) with Gaffer.BlockedConnection(self.__currentChangedConnection): self.__multiSelectionMenu.setSelection( self.__multiSelectionMenu[-1])
def testContextChangedAndGIL( self ) : script = Gaffer.ScriptNode() script["plane"] = GafferScene.Plane() script["plane"]["divisions"].setValue( imath.V2i( 20 ) ) script["sphere"] = GafferScene.Sphere() script["expression"] = Gaffer.Expression() script["expression"].setExpression( "parent['sphere']['radius'] = context.get( 'minRadius', 0.1 ) + context.getFrame()" ) script["instancer"] = GafferScene.Instancer() script["instancer"]["in"].setInput( script["plane"]["out"] ) script["instancer"]["instances"].setInput( script["sphere"]["out"] ) script["instancer"]["parent"].setValue( "/plane" ) context = Gaffer.Context() traverseConnection = Gaffer.ScopedConnection( GafferSceneTest.connectTraverseSceneToContextChangedSignal( script["instancer"]["out"], context ) ) with context : context.setFrame( 10 ) context.setFramesPerSecond( 50 ) context.setTime( 1 ) context.set( "a", 1 ) context.set( "a", 2.0 ) context.set( "a", "a" ) context.set( "a", imath.V2i() ) context.set( "a", imath.V3i() ) context.set( "a", imath.V2f() ) context.set( "a", imath.V3f() ) context.set( "a", imath.Color3f() ) context.set( "a", IECore.BoolData( True ) ) context["b"] = 1 context["b"] = 2.0 context["b"] = "b" context["b"] = imath.V2i() context["b"] = imath.V3i() context["b"] = imath.V2f() context["b"] = imath.V3f() context["b"] = imath.Color3f() context["b"] = IECore.BoolData( True ) with Gaffer.BlockedConnection( traverseConnection ) : # Must add it with the connection disabled, otherwise # the addition causes a traversal, and then remove() gets # all its results from the cache. context["minRadius"] = 0.2 context.remove( "minRadius" ) with Gaffer.BlockedConnection( traverseConnection ) : context["minRadius"] = 0.3 del context["minRadius"]
def __updateKeyValue(self): # if multiple keys selected display "---" unless all selected keys have same value selectedKeys = self.parent().curveGadget().selectedKeys() value = sole(key.getValue() for key in selectedKeys) if value is not None: with Gaffer.BlockedConnection(self.__valueConnection): self.__valueEditor.setValue(value) else: with Gaffer.BlockedConnection(self.__valueConnection): self.__valueEditor.setText("") self.__valueEditor._qtWidget().setPlaceholderText("---") # set disabled when no selected keys self.__valueEditor.setEnabled(bool(selectedKeys))
def __updateFilter(self): newFilter = self.__path.getFilter() if self.__filter is not None and self.__filter.isSame(newFilter): return # update the directory path filter to include # the new filter, but with the additional removal # of leaf paths. block the signal because otherwise # we'd end up truncating the main path in __dirPathChanged. with Gaffer.BlockedConnection(self.__dirPathChangedConnection): if newFilter is not None: self.__dirPath.setFilter( Gaffer.CompoundPathFilter([ Gaffer.LeafPathFilter(), newFilter, ])) else: self.__dirPath.setFilter(Gaffer.LeafPathFilter()) # update ui for displaying the filter newFilterUI = None if newFilter is not None: newFilterUI = GafferUI.PathFilterWidget.create(newFilter) self.__filterFrame.setChild(newFilterUI) self.__filterFrame.setVisible(newFilterUI is not None) self.__filter = newFilter
def _updateFromPlug( self ) : plug = self.getPlug() if plug is not None : with self.getContext() : try : value = plug.getValue() except : value = None if value is not None : with Gaffer.BlockedConnection( self.__valueChangedConnection ) : self.__numericWidget.setValue( value ) self.__numericWidget.setErrored( value is None ) ## \todo Perhaps this styling should be provided by the NumericWidget itself? animated = Gaffer.Animation.isAnimated( plug ) widgetAnimated = GafferUI._Variant.fromVariant( self.__numericWidget._qtWidget().property( "gafferAnimated" ) ) or False if widgetAnimated != animated : self.__numericWidget._qtWidget().setProperty( "gafferAnimated", GafferUI._Variant.toVariant( bool( animated ) ) ) self.__numericWidget._repolish() self.__numericWidget.setEditable( self._editable( canEditAnimation = True ) )
def __setPlugValue( self, mergeGroup="" ) : with Gaffer.UndoScope( self.getPlug().ancestor( Gaffer.ScriptNode ), mergeGroup=mergeGroup ) : with Gaffer.BlockedConnection( self._plugConnections() ) : if Gaffer.Animation.isAnimated( self.getPlug() ) : curve = Gaffer.Animation.acquire( self.getPlug() ) if self.__numericWidget.getText() != self.__numericWidget.valueToString( curve.evaluate( self.getContext().getTime() ) ) : curve.addKey( Gaffer.Animation.Key( self.getContext().getTime(), self.__numericWidget.getValue(), Gaffer.Animation.Type.Linear ) ) else : try : self.getPlug().setValue( self.__numericWidget.getValue() ) except : pass # now any changes that were made in the numeric widget have been transferred # into the global undo queue, we remove the text editing changes from the # widget's private text editing undo queue. it will then ignore undo shortcuts, # allowing them to fall through to the global undo shortcut. self.__numericWidget.clearUndo() # we always need to update the ui from the plug after trying to set it, # because the plug might clamp the value to something else. furthermore # it might not even emit plugSetSignal if it happens to clamp to the same # value as it had before. we block calls to _updateFromPlug() while setting # the value to avoid having to do the work twice if plugSetSignal is emitted. self._updateFromPlug()
def __transferSelectionFromContext(self): selection = ContextAlgo.getSelectedPaths(self.getContext()) with Gaffer.BlockedConnection(self.__selectionChangedConnection): self.__pathListing.setSelection(selection, scrollToFirst=True, expandNonLeaf=False)
def __selectionChanged(self, pathListing): assert (pathListing is self.__pathListing) with Gaffer.BlockedConnection(self._contextChangedConnection()): ContextAlgo.setSelectedPaths(self.getContext(), pathListing.getSelection())
def __pathListingSelectionChanged(self, pathListing): selection = pathListing.getSelectedPaths() if len(selection): with Gaffer.BlockedConnection( self.__attributeCachePathChangedConnection): self.__attributeCachePath[:] = selection[0][:]
def appendMessage(self, level, context, message): # make sure relevant button is shown self.__levelButtons[level].setVisible(True) # append message text formatted = "<h1 class='%s'>%s : %s </h1><span class='message'>%s</span><br>" % ( IECore.Msg.levelAsString(level), IECore.Msg.levelAsString(level), context, message.replace("\n", "<br>")) with Gaffer.BlockedConnection(self.__textChangedConnection): self.__text._qtWidget().appendHtml(formatted) # Update the gui so messages are output as they occur, rather than all getting queued # up till the end. We have to be careful to avoid recursion when doing this - another # thread may be queuing up loads of messages using self.messageHandler(), and those # will get processed by processEvents(), resulting in a recursive call to appendMessage(). # If the other thread supplies messages fast enough and we don't guard against recursion # then we can end up exceeding Python's stack limit very quickly. if not self.__processingEvents: try: self.__processingEvents = True QtGui.QApplication.instance().processEvents( QtCore.QEventLoop.ExcludeUserInputEvents) finally: self.__processingEvents = False
def __tabMoved( self, fromIndex, toIndex ) : with Gaffer.BlockedConnection( self.__plugMetadataChangedConnection ) : with Gaffer.UndoScope( self.__rowsPlug.ancestor( Gaffer.ScriptNode ) ) : for i in range( 0, self._qtWidget().count() ) : sectionName = self._qtWidget().tabText( i ) Gaffer.Metadata.registerValue( self.__rowsPlug, "spreadsheet:section:{}:index".format( sectionName ), i )
def __pathListingSelectionChanged(self, pathListing): selection = pathListing.getSelection() if not selection.isEmpty(): with Gaffer.BlockedConnection( self.__indexedIOPathChangedConnection): self.__indexedIOPath.setFromString(selection.paths()[0])
def __setPlugValues(self, mergeGroup=""): with Gaffer.UndoScope(next(iter(self.getPlugs())).ancestor( Gaffer.ScriptNode), mergeGroup=mergeGroup): with Gaffer.BlockedConnection(self._plugConnections()): for plug in self.getPlugs(): if Gaffer.Animation.isAnimated(plug): curve = Gaffer.Animation.acquire(plug) if self.__numericWidget.getText( ) != self.__numericWidget.valueToString( curve.evaluate(self.getContext().getTime())): curve.addKey( Gaffer.Animation.Key( self.getContext().getTime(), self.__numericWidget.getValue(), Gaffer.Animation.Type.Linear)) else: try: plug.setValue(self.__sliders.getColor()) except Exception as e: print(e) pass # We always need to update the UI from the plugs after trying to set them, # because the plugs might clamp the value to something else. Furthermore # they might not even emit `plugDirtiedSignal() if they happens to clamp to the same # value as before. We block calls to `_updateFromPlugs()` while setting # the value to avoid having to do the work twice if `plugDirtiedSignal()` _is_ emitted. self._updateFromPlugs()
def __pathChanged(self, path): self.__updateFilter() # update the directory path and the listing path, but only if we're # in list mode rather than tree mode. if self.__directoryListing.getDisplayMode( ) == GafferUI.PathListingWidget.DisplayMode.List: pathCopy = path.copy() if pathCopy.isLeaf(): del pathCopy[-1] pathCopy.truncateUntilValid() with Gaffer.BlockedConnection( (self.__dirPathChangedConnection, self.__listingPathChangedConnection)): self.__dirPath.setFromPath(pathCopy) self.__listingPath.setFromPath(pathCopy) else: # if we're in tree mode then we instead scroll to display the new path self.__directoryListing.scrollToPath(path) # and update the selection in the listing if path.isLeaf(): self.__directoryListing.setSelection( IECore.PathMatcher([str(path)])) else: self.__directoryListing.setSelection(IECore.PathMatcher())
def __setPlugValues( self, mergeGroup="" ) : with Gaffer.UndoScope( next( iter( self.getPlugs() ) ).ancestor( Gaffer.ScriptNode ), mergeGroup=mergeGroup ) : with Gaffer.BlockedConnection( self._plugConnections() ) : for plug in self.getPlugs() : if Gaffer.Animation.isAnimated( plug ) : curve = Gaffer.Animation.acquire( plug ) if self.__numericWidget.getText() != self.__numericWidget.valueToString( curve.evaluate( self.getContext().getTime() ) ) : curve.insertKey( self.getContext().getTime(), self.__numericWidget.getValue() ) else : try : plug.setValue( self.__numericWidget.getValue() ) except : pass # Now any changes that were made in the numeric widget have been transferred # into the global undo queue, we remove the text editing changes from the # widget's private text editing undo queue. It will then ignore undo shortcuts, # allowing them to fall through to the global undo shortcut. self.__numericWidget.clearUndo() # We always need to update the UI from the plugs after trying to set them, # because the plugs might clamp the value to something else. Furthermore # they might not even emit `plugDirtiedSignal() if they happens to clamp to the same # value as before. We block calls to `_updateFromPlugs()` while setting # the value to avoid having to do the work twice if `plugDirtiedSignal()` _is_ emitted. self._updateFromPlugs()
def _updateFromPlug(self): if self.getPlug() is not None: value = None with self.getContext(): # Since BoolWidget doesn't yet have a way of # displaying errors, we just ignore exceptions # and leave UI components like GraphGadget to # display them via Node.errorSignal(). with IECore.IgnoredExceptions(Exception): value = self.getPlug().getValue() if value is not None: with Gaffer.BlockedConnection(self.__stateChangedConnection): self.__boolWidget.setState(value) displayMode = Gaffer.Metadata.plugValue( self.getPlug(), "boolPlugValueWidget:displayMode") if displayMode is not None: self.__boolWidget.setDisplayMode( self.__boolWidget.DisplayMode.Switch if displayMode == "switch" else self.__boolWidget.DisplayMode.CheckBox) self.__boolWidget.setEnabled(self._editable(canEditAnimation=True))
def __setScale(self, direction, widget, reason): # check for invalid edit if reason == GafferUI.NumericWidget.ValueChangedReason.InvalidEdit: self.__updateTangentScale(direction) return # handle undo queue if not widget.changesShouldBeMerged( self.__lastChangedReasonScale[direction], reason): self.__mergeGroupIdScale[direction] += 1 self.__lastChangedReasonScale[direction] = reason # set scale for all selected keys in specified direction selectedKeys = self.parent().curveGadget().selectedKeys() if selectedKeys: try: value = max(widget.getValue(), float(0)) except ValueError: return with Gaffer.UndoScope( selectedKeys[0].parent().ancestor(Gaffer.ScriptNode), mergeGroup=str(self.__mergeGroupIdScale[direction])): for key in selectedKeys: with Gaffer.BlockedConnection( self.__connections[key.parent()].tangent): key.tangent(direction).setScale(value) widget.clearUndo() # ensure editors are up to date for direction in Gaffer.Animation.Direction.names.values(): self.__updateTangentScale(direction)
def __dataChanged( self, widget ) : assert( widget is self.__dataWidget ) with Gaffer.UndoContext( self.getPlug().ancestor( Gaffer.ScriptNode ) ) : with Gaffer.BlockedConnection( self._plugConnections() ) : self.getPlug().setValue( self.__dataWidget.getData()[0] )
def __setValue(self, widget, reason): # check for invalid edit if reason == GafferUI.NumericWidget.ValueChangedReason.InvalidEdit: self.__updateKeyValue() return # handle undo queue if not widget.changesShouldBeMerged(self.__lastChangedReasonValue, reason): self.__mergeGroupIdValue += 1 self.__lastChangedReasonValue = reason # set value for all selected keys selectedKeys = self.parent().curveGadget().selectedKeys() if selectedKeys: try: value = widget.getValue() except ValueError: return with Gaffer.UndoScope(selectedKeys[0].parent().ancestor( Gaffer.ScriptNode), mergeGroup=str(self.__mergeGroupIdValue)): for key in selectedKeys: with Gaffer.BlockedConnection( self.__connections[key.parent()].value): key.setValue(value) widget.clearUndo()
def __selectionChanged(self, pathListing): assert (pathListing is self.__curveList) paths = pathListing.getSelectedPaths() plugList = [] for path in paths: graphComponent = path.property("graphComponent:graphComponent") if isinstance(graphComponent, Gaffer.ValuePlug ) and Gaffer.Animation.isAnimated(graphComponent): plugList.append(graphComponent) for child in graphComponent.children(): if isinstance(child, Gaffer.ValuePlug ) and Gaffer.Animation.isAnimated(child): plugList.append(child) self.__editablePlugs = set(plugList) editable = self.__animationGadget.editablePlugs() with Gaffer.BlockedConnection(self.__editablePlugsConnections): editable.clear() for plug in plugList: editable.add(self.__sourceCurvePlug(plug))
def __expansionChanged(self, pathListing): assert (pathListing is self.__curveList) paths = pathListing.getExpandedPaths() visiblePlugs = set() for path in paths: for childPath in path.children(): child = childPath.property("graphComponent:graphComponent") if isinstance(child, Gaffer.ValuePlug ) and Gaffer.Animation.isAnimated(child): visiblePlugs.add(child) visible = self.__animationGadget.visiblePlugs() editable = self.__animationGadget.editablePlugs() visible.clear() for plug in visiblePlugs: visible.add(self.__sourceCurvePlug(plug)) with Gaffer.BlockedConnection(self.__editablePlugsConnections): editable.clear() for plug in (self.__editablePlugs or set()) & visiblePlugs: editable.add(self.__sourceCurvePlug(plug))
def __selectionChanged(self, pathListing): assert (pathListing is self.__curveList) paths = pathListing.getSelectedPaths() plugList = [] for path in paths: graphComponent = self.__scriptNode.descendant( str(path).replace('/', '.')) if isinstance(graphComponent, Gaffer.ValuePlug ) and Gaffer.Animation.isAnimated(graphComponent): plugList.append(graphComponent) for child in graphComponent.children(): if isinstance(child, Gaffer.ValuePlug ) and Gaffer.Animation.isAnimated(child): plugList.append(child) self.__editablePlugs = set(plugList) editable = self.__animationGadget.editablePlugs() with Gaffer.BlockedConnection(self.__editablePlugAddedConnection): editable.clear() for plug in plugList: if plug in editable: continue curvePlug = self.connectedCurvePlug(plug) if curvePlug: editable.add(curvePlug)
def __dirPathChanged( self, dirPath ) : # update the main path and the listing path dirPathCopy = dirPath.copy() dirPathCopy.truncateUntilValid() with Gaffer.BlockedConnection( ( self.__pathChangedConnection, self.__listingPathChangedConnection ) ) : self.__path[:] = dirPathCopy[:] self.__listingPath[:] = dirPathCopy[:]
def __selectionChanged( self, pathListing ) : assert( pathListing is self.__pathListing ) paths = pathListing.getSelectedPaths() paths = IECore.StringVectorData( [ str( path ) for path in paths ] ) with Gaffer.BlockedConnection( self._contextChangedConnection() ) : self.getContext().set( "ui:scene:selectedPaths", paths )
def __listingPathChanged( self, listingPath ) : assert( listingPath is self.__listingPath ) # update the directory path and the main path with Gaffer.BlockedConnection( ( self.__pathChangedConnection, self.__dirPathChangedConnection ) ) : self.__dirPath[:] = listingPath[:] self.__path[:] = listingPath[:]
def __playbackFrameRangeChanged( self, playback ) : minValue, maxValue = playback.getFrameRange() with Gaffer.BlockedConnection( ( self.__sliderRangeStartChangedConnection, self.__sliderRangeEndChangedConnection ) ) : self.__slider.setRange( minValue, maxValue ) self.__sliderRangeStart.setValue( minValue ) self.__sliderRangeEnd.setValue( maxValue )
def _updateFromPlug(self): if self.getPlug() is not None: with self.getContext(): with Gaffer.BlockedConnection(self.__stateChangedConnection): self.__checkBox.setState(self.getPlug().getValue()) self.__checkBox.setEnabled(self._editable())
def __transferExpansionFromContext( self ) : expandedPaths = ContextAlgo.getExpandedPaths( self.getContext() ) if expandedPaths is None : return with Gaffer.BlockedConnection( self.__expansionChangedConnection ) : self.__pathListing.setExpansion( expandedPaths )