Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscellaneous ContextTracker-related improvements #6058

Merged
merged 5 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Improvements
- Instancer :
- Improved Arnold raytracing performance for encapsulated instancers with many prototypes. All instances are now output in a single top-level procedural rather than a top-level procedural per prototype, resulting in more optimal BVH traversals in Arnold.
- Reduced scene generation time for encapsulated instancers by around 20%.
- NodeEditor : Added <kbd>Alt</kbd> + middle-click action for showing context variable substitutions in strings.
- LightEditor, RenderPassEditor : History windows now use a context determined relative to the current focus node.

Fixes
-----
Expand All @@ -18,6 +20,11 @@ Fixes
- Fixed partial image updates when an unrelated InteractiveRender was running (#6043).
- Fixed "colour tearing", where updates to some image channels became visible before updates to others.
- Fixed unnecessary texture updates when specific image tiles don't change.
- GraphEditor :
- Fixed lingering error badges (#3820).
- RenderPassEditor :
- Fixed history window to update on context changes, for example, when the current frame is changed.
- Fixed invalid `scene:path` context variables created by the history window. [^1]

Breaking Changes
----------------
Expand Down
45 changes: 18 additions & 27 deletions doc/examples/rendering/renderPassEditorArnold.gfr

Large diffs are not rendered by default.

41 changes: 18 additions & 23 deletions python/GafferSceneUI/_HistoryWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,17 @@ def headerData( self, canceller = None ) :

class _NodeNameColumn( GafferUI.PathColumn ) :

def __init__( self, title, property, scriptNode ) :
def __init__( self, title, property ) :

GafferUI.PathColumn.__init__( self )

self.__title = title
self.__property = property
self.__scriptNode = scriptNode

def cellData( self, path, canceller = None ) :

cellValue = path.property( self.__property )

data = self.CellData( cellValue.relativeName( self.__scriptNode ) )

return data
node = path.property( self.__property )
return self.CellData( node.relativeName( node.scriptNode() ) )

def headerData( self, canceller = None ) :

Expand Down Expand Up @@ -136,24 +132,21 @@ def headerData( self, canceller = None ) :

class _HistoryWindow( GafferUI.Window ) :

def __init__( self, inspector, scenePath, context, scriptNode, title=None, **kw ) :

assert( isinstance( scriptNode, Gaffer.ScriptNode ) )
def __init__( self, inspector, inspectionPath, title=None, **kw ) :

if title is None :
title = "History"

GafferUI.Window.__init__( self, title, **kw )

self.__inspector = inspector
self.__scenePath = scenePath
self.__scriptNode = scriptNode
self.__inspectionPath = inspectionPath

with self :
self.__pathListingWidget = GafferUI.PathListingWidget(
Gaffer.DictPath( {}, "/" ),
columns = (
_NodeNameColumn( "Node", "history:node", self.__scriptNode ),
_NodeNameColumn( "Node", "history:node" ),
_ValueColumn( "Value", "history:value", "history:fallbackValue" ),
_OperationIconColumn( "Operation", "history:operation" ),
),
Expand All @@ -175,17 +168,19 @@ def __init__( self, inspector, scenePath, context, scriptNode, title=None, **kw

inspector.dirtiedSignal().connect( Gaffer.WeakMethod( self.__inspectorDirtied ) )

context.changedSignal().connect( Gaffer.WeakMethod( self.__contextChanged ) )

self.__updatePath( context )
## \todo We want to make the inspection framework scene-agnostic. We could add an `Inspector::plug()` method
# to provide a scene-agnostic way of querying what is being inspected, and use it here.
self.__contextTracker = GafferUI.ContextTracker.acquireForFocus( self.__inspectionPath.getScene() )
self.__contextTracker.changedSignal().connect( Gaffer.WeakMethod( self.__contextChanged ) )
self.__updatePath()

def __updatePath( self, newContext ) :
def __updatePath( self ) :

with Gaffer.Context( newContext ) as context :
context["scene:path"] = GafferScene.ScenePlug.stringToPath( self.__scenePath )
self.__inspectionPath.setContext( self.__contextTracker.context( self.__inspectionPath.getScene() ) )
with self.__inspectionPath.inspectionContext() :
self.__path = self.__inspector.historyPath()
self.__pathChangedConnection = self.__path.pathChangedSignal().connect( Gaffer.WeakMethod( self.__pathChanged ), scoped = True )

self.__pathChangedConnection = self.__path.pathChangedSignal().connect( Gaffer.WeakMethod( self.__pathChanged ), scoped = True )
self.__pathListingWidget.setPath( self.__path )

def __pathChanged( self, path ) :
Expand Down Expand Up @@ -277,9 +272,9 @@ def __inspectorDirtied( self, inspector ) :

self.__path._emitPathChanged()

def __contextChanged( self, context, key ) :
def __contextChanged( self, contextTracker ) :

self.__updatePath( context )
self.__updatePath()

def __updateFinished( self, pathListing ) :

Expand All @@ -291,7 +286,7 @@ def __updateFinished( self, pathListing ) :
# The node and all of its parents up to the script node
# contribute to the path name.

while node is not self.__scriptNode and node is not None :
while node is not None and not isinstance( node, Gaffer.ScriptNode ) :
self.__nodeNameChangedSignals.append(
node.nameChangedSignal().connect(
Gaffer.WeakMethod( self.__nodeNameChanged ),
Expand Down
10 changes: 3 additions & 7 deletions python/GafferSceneUI/_InspectorColumn.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,20 +329,16 @@ def __showHistory( pathListing ) :

columns = pathListing.getColumns()
selection = pathListing.getSelection()
path = pathListing.getPath().copy()

for i, column in enumerate( columns ) :
for pathString in selection[i].paths() :
path = pathListing.getPath().copy()
path.setFromString( pathString )
inspectionContext = path.inspectionContext()
if inspectionContext is None :
if path.inspectionContext() is None :
continue

window = _HistoryWindow(
column.inspector(),
pathString,
inspectionContext,
pathListing.ancestor( GafferUI.Editor ).scriptNode(),
path,
"History : {} : {}".format( pathString, column.headerData().value )
)
pathListing.ancestor( GafferUI.Window ).addChildWindow( window, removeOnClose = True )
Expand Down
15 changes: 9 additions & 6 deletions python/GafferUI/PathPlugValueWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import GafferUI

from .PlugValueWidget import sole
from .StringPlugValueWidget import addSubstitutionsPopup

import os

Expand All @@ -67,13 +68,14 @@ def __init__( self, plug, path=None, pathChooserDialogueKeywords=None, **kw ) :

self.__pathChooserDialogueKeywords = pathChooserDialogueKeywords

pathWidget = GafferUI.PathWidget( self.__path )
self._addPopupMenu( pathWidget )
self.__row.append( pathWidget )
with self.__row :

button = GafferUI.Button( image = "pathChooser.png", hasFrame=False )
button.clickedSignal().connect( Gaffer.WeakMethod( self.__buttonClicked ) )
self.__row.append( button )
pathWidget = GafferUI.PathWidget( self.__path )
self._addPopupMenu( pathWidget )
addSubstitutionsPopup( pathWidget )

button = GafferUI.Button( image = "pathChooser.png", hasFrame=False )
button.clickedSignal().connect( Gaffer.WeakMethod( self.__buttonClicked ) )

pathWidget.editingFinishedSignal().connect( Gaffer.WeakMethod( self.__setPlugValue ) )

Expand Down Expand Up @@ -102,6 +104,7 @@ def getToolTip( self ) :
result += "- <kbd>Tab</kbd> to autocomplete path component\n"
result += "- Select path component (or hit <kbd>&darr;</kbd>) to show path-level contents menu\n"
result += "- Select all to show path hierarchy menu\n"
result += "- <kbd>Alt</kbd> + middle-click to show context variable substitutions\n"

return result

Expand Down
70 changes: 70 additions & 0 deletions python/GafferUI/StringPlugValueWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#
##########################################################################

import functools

import IECore

import Gaffer
Expand All @@ -59,6 +61,7 @@ def __init__( self, plugs, **kw ) :
self._addPopupMenu( self.__textWidget )

self.__textWidget.keyPressSignal().connect( Gaffer.WeakMethod( self.__keyPress ) )
addSubstitutionsPopup( self.__textWidget )
self.__textWidget.editingFinishedSignal().connect( Gaffer.WeakMethod( self.__editingFinished ) )
self.__textChangedConnection = self.__textWidget.textChangedSignal().connect( Gaffer.WeakMethod( self.__textChanged ) )

Expand All @@ -71,6 +74,15 @@ def setHighlighted( self, highlighted ) :
GafferUI.PlugValueWidget.setHighlighted( self, highlighted )
self.textWidget().setHighlighted( highlighted )

def getToolTip( self ) :

result = GafferUI.PlugValueWidget.getToolTip( self )

result += "\n## Actions\n"
result += " - <kbd>Alt</kbd> + middle-click to show context variable substitutions\n"

return result

def _updateFromValues( self, values, exception ) :

value = sole( values )
Expand Down Expand Up @@ -186,3 +198,61 @@ def __setPlugValues( self ) :
self.__textWidget.clearUndo()

GafferUI.PlugValueWidget.registerType( Gaffer.StringPlug, StringPlugValueWidget )

# Substitutions Popup
# ===================

def addSubstitutionsPopup( widget ) :

widget.buttonPressSignal().connect( __substitutionsButtonPress )
widget.buttonReleaseSignal().connect( __substitutionsButtonRelease )

def __substitutionsCopyClicked( widget, text ) :

application = widget.ancestor( GafferUI.PlugValueWidget ).scriptNode().ancestor( Gaffer.ApplicationRoot )
application.setClipboardContents( IECore.StringData( text ) )
widget.ancestor( GafferUI.Window ).close()
return True

def __substitutionsDragBegin( widget, event, text ) :

GafferUI.Pointer.setCurrent( "values" )
return text

def __substitutionsDragEnd( widget, event ) :

GafferUI.Pointer.setCurrent( None )
return True

def __substitutionsButtonPress( widget, event ) :

if event.buttons != event.Buttons.Middle or event.modifiers != event.Modifiers.Alt :
return False

# Alt + Middle click

plugValueWidget = widget.ancestor( GafferUI.PlugValueWidget )
substitutions = sole( p.substitutions() for p in plugValueWidget.getPlugs() )
if substitutions is None :
return True

text = plugValueWidget.context().substitute( widget.getText(), substitutions )
if text == widget.getText() :
return True

with GafferUI.PopupWindow() as widget.__substitutionsPopupWindow :
with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, 4 ) :
label = GafferUI.Label( f"<b>{text}</b>" )
label.buttonPressSignal().connect( lambda widget, event : True )
label.dragBeginSignal().connect( functools.partial( __substitutionsDragBegin, text = text ) )
label.dragEndSignal().connect( __substitutionsDragEnd )
button = GafferUI.Button( image = "duplicate.png", hasFrame = False, toolTip = "Copy Text" )
button.clickedSignal().connect( functools.partial( __substitutionsCopyClicked, text = text ) )

widget.__substitutionsPopupWindow.popup( parent = widget )
return True

def __substitutionsButtonRelease( widget, event ) :

# Take event to stop Alt+Middle from pasting on Linux.
return event.button == event.Buttons.Middle and event.modifiers == event.Modifiers.Alt
22 changes: 16 additions & 6 deletions resources/graphics.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading