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

OCIO/ContextTracker fixes #6080

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
142 changes: 86 additions & 56 deletions python/GafferImageUI/OpenColorIOConfigPlugUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
import GafferImage
import GafferImageUI

from GafferUI.PlugValueWidget import sole

# General OpenColorIOConfigPlug UI metadata

Gaffer.Metadata.registerValue( GafferImage.OpenColorIOConfigPlug, "plugValueWidget:type", "GafferUI.LayoutPlugValueWidget" )
Expand Down Expand Up @@ -156,21 +158,58 @@ def __init__( self, plugs, **kw ) :
self.__menuButton = GafferUI.MenuButton( "", menu = GafferUI.Menu( Gaffer.WeakMethod( self.__menuDefinition ) ) )
GafferUI.PlugValueWidget.__init__( self, self.__menuButton, plugs, **kw )

self.context().changedSignal().connect( Gaffer.WeakMethod( self.__contextChanged ), scoped = True )
self.__ensureValidValue()
self.__currentValue = None
self.__currentDisplay = None
self.__currentView = None

## Returns `display, view, valid`.
@staticmethod
def parseValue( value ) :

assert( isinstance( value, str ) )

config = GafferImage.OpenColorIOAlgo.currentConfig()
if value == "__default__" :
# Use the default from the config.
display = config.getDefaultDisplay()
view = config.getDefaultView( display )
valid = True
else :
elements = value.split( "/" )
assert( len( elements ) == 2 )
display, view = elements[0], elements[1]
valid = display in config.getDisplays() and view in config.getViews( display )

return display, view, valid

def _updateFromValues( self, values, exception ) :

if exception is not None :
self.__menuButton.setText( "" )
self.__menuButton.setErrored( True )
self.__currentValue = None
self.__currentDisplay = None
self.__currentView = None
else :
assert( len( values ) == 1 )
# Only show the View name, because the Display name is more of
# a "set once and forget" affair. The menu shows both for when
# you need to check.
self.__menuButton.setText( values[0].partition( "/" )[-1] )

self.__menuButton.setErrored( exception is not None )
self.__currentValue = sole( values )
if self.__currentValue is None :
self.__menuButton.setText( "---" )
self.__menuButton.setErrored( not all( self.parseValue( v )[2] for v in values ) )
self.__currentDisplay = None
self.__currentView = None
else :
self.__currentDisplay, self.__currentView, valid = self.parseValue( self.__currentValue )
# Only show the View name, because the Display name is more of
# a "set once and forget" affair. The menu shows both for when
# you need to check.
self.__menuButton.setText( self.__currentView )
self.__menuButton.setErrored( not valid )

def _valuesDependOnContext( self ) :

# We use `OpenColorIOAlgo.currentConfig()` in `_updateFromValues()`,
# so are always dependent on the context.
return True

def _updateFromEditable( self ) :

Expand All @@ -189,19 +228,18 @@ def __menuDefinition( self ) :
result.append( "/Invalid Config", { "active" : False } )
return result

currentDisplay, currentView = self.getPlug().getValue().split( "/" )

# View section

result.append( "/__ViewDivider__", { "divider" : True, "label" : "View" } )

for view in config.getViews( currentDisplay ) :
display = self.__currentDisplay if self.__currentDisplay in config.getDisplays() else config.getDefaultDisplay()
for view in config.getViews( display ) :
if not IECore.StringAlgo.matchMultiple( view, activeViews ) :
continue
result.append(
f"/{view}", {
"command" : functools.partial( Gaffer.WeakMethod( self.__setValue ), currentDisplay, view ),
"checkBox" : view == currentView
"command" : functools.partial( Gaffer.WeakMethod( self.__setValue ), display, view ),
"checkBox" : view == self.__currentView
}
)

Expand All @@ -210,14 +248,26 @@ def __menuDefinition( self ) :
result.append( "/__DisplayDivider__", { "divider" : True, "label" : "Display" } )

for display in config.getDisplays() :
view = currentView if currentView in config.getViews( display ) else config.getDefaultView( display )
view = self.__currentView if self.__currentView in config.getViews( display ) else config.getDefaultView( display )
result.append(
f"/{display}", {
"command" : functools.partial( Gaffer.WeakMethod( self.__setValue ), display, view ),
"checkBox" : display == currentDisplay
"checkBox" : display == self.__currentDisplay
}
)

# Default section

result.append( "/__OptionsDivider__", { "divider" : True, "label" : "Options" } )

result.append(
f"/Use Default Display And View", {
"command" : functools.partial( Gaffer.WeakMethod( self.__setToDefault ) ),
"checkBox" : self.__currentValue == "__default__",
"description" : "Always uses the default display and view for the current config. Useful when changing configs often, or using context-sensitive configs."
}
)

return result

def __setValue( self, display, view, *unused ) :
Expand All @@ -227,38 +277,15 @@ def __setValue( self, display, view, *unused ) :
functools.partial( _viewDisplayTransformCreator, display, view )
)

with Gaffer.UndoScope( self.getPlug().ancestor( Gaffer.ScriptNode ) ) :
self.getPlug().setValue( f"{display}/{view}" )

def __contextChanged( self, context, key ) :

if key.startswith( "ocio:" ) :
# OCIO config may have changed, and the new config might
# not provide the display transform that we want.
self.__ensureValidValue()
with Gaffer.UndoScope( self.scriptNode() ) :
for plug in self.getPlugs() :
self.getPlug().setValue( f"{display}/{view}" )

def __ensureValidValue( self ) :
def __setToDefault( self, *unused ) :

with self.context() :
try :
config = GafferImage.OpenColorIOAlgo.currentConfig()
except :
return
elements = self.getPlug().getValue().split( "/" )

if len( elements ) == 2 :
display, view = elements[0], elements[1]
else :
display = config.getDefaultDisplay()
view = config.getDefaultView( display )

if display not in config.getDisplays() :
display = config.getDefaultDisplay()

if view not in config.getViews( display ) :
view = config.getDefaultView( display )

self.__setValue( display, view )
with Gaffer.UndoScope( self.scriptNode() ) :
for plug in self.getPlugs() :
self.getPlug().setValue( "__default__" )

# Connection between default script config and Widget and View display transforms.
# Calling `connect()` from an application startup file is what makes the UI OpenColorIO-aware.
Expand All @@ -270,6 +297,10 @@ def connect( script ) :
if not hadPlug :
Gaffer.NodeAlgo.applyUserDefaults( plug )

GafferUI.View.DisplayTransform.registerDisplayTransform(
"__default__", __defaultViewDisplayTransformCreator
)

script.plugDirtiedSignal().connect( __scriptPlugDirtied )
__scriptPlugDirtied( plug )

Expand All @@ -289,6 +320,12 @@ def _viewDisplayTransformCreator( display, view ) :
processor = __displayTransformProcessor( config, context, workingSpace, display, view )
return GafferImageUI.OpenColorIOAlgo.displayTransformToFramebufferShader( processor )

def __defaultViewDisplayTransformCreator() :

config = GafferImage.OpenColorIOAlgo.currentConfig()
display = config.getDefaultDisplay()
return _viewDisplayTransformCreator( display, config.getDefaultView( display ) )

def __widgetDisplayTransform( config, context, workingSpace, display, view ) :

try :
Expand All @@ -315,6 +352,7 @@ def __scriptPlugDirtied( plug ) :
with plug.parent().context() :
try :
config, context = GafferImage.OpenColorIOAlgo.currentConfigAndContext()
currentDisplay, currentView, currentValid = DisplayTransformPlugValueWidget.parseValue( plug["displayTransform"].getValue() )
except :
return

Expand All @@ -325,15 +363,7 @@ def __scriptPlugDirtied( plug ) :
functools.partial( _viewDisplayTransformCreator, display, view )
)

displayTransform = plug["displayTransform"].getValue()
if displayTransform :
display, view = displayTransform.split( "/" )
else :
display = config.getDefaultDisplay()
view = config.getDefaultView( display )

workingSpace = GafferImage.OpenColorIOAlgo.getWorkingSpace( plug.parent().context() )

scriptWindow = GafferUI.ScriptWindow.acquire( plug.parent(), createIfNecessary = False )
if scriptWindow is not None :
scriptWindow.setDisplayTransform( __widgetDisplayTransform( config, context, workingSpace, display, view ) )
workingSpace = GafferImage.OpenColorIOAlgo.getWorkingSpace( plug.parent().context() )
scriptWindow.setDisplayTransform( __widgetDisplayTransform( config, context, workingSpace, currentDisplay, currentView ) )
6 changes: 6 additions & 0 deletions python/GafferUI/ColorChooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ def xyAxes( self ) :

return xAxis, yAxis

def _displayTransformChanged( self ) :

GafferUI.Widget._displayTransformChanged( self )
self.__colorFieldToDraw = None
self._qtWidget().update()

def __setColorInternal( self, color, staticComponent, reason ) :

dragBeginOrEnd = reason in ( GafferUI.Slider.ValueChangedReason.DragBegin, GafferUI.Slider.ValueChangedReason.DragEnd )
Expand Down
2 changes: 1 addition & 1 deletion src/GafferImage/OpenColorIOConfigPlug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ OpenColorIOConfigPlug::OpenColorIOConfigPlug( const std::string &name, Direction
addChild( new StringPlug( "config", direction, "", childFlags ) );
addChild( new StringPlug( "workingSpace", direction, OCIO_NAMESPACE::ROLE_SCENE_LINEAR, childFlags ) );
addChild( new ValuePlug( "variables", direction, childFlags ) );
addChild( new StringPlug( "displayTransform", direction, "", childFlags ) );
addChild( new StringPlug( "displayTransform", direction, "__default__", childFlags ) );
}

Gaffer::StringPlug *OpenColorIOConfigPlug::configPlug()
Expand Down
2 changes: 1 addition & 1 deletion src/GafferUI/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,9 +548,9 @@ void View::DisplayTransform::preRender()
{
m_shader = nullptr;
const std::string name = namePlug()->getValue();
auto it = displayTransformCreators().find( name );
if( !name.empty() )
{
auto it = displayTransformCreators().find( name );
if( it != displayTransformCreators().end() )
{
Context::Scope scope( view()->context() );
Expand Down
1 change: 1 addition & 0 deletions startup/gui/ocio.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def __scriptAdded( container, script ) :

Gaffer.Metadata.registerValue( GafferUI.View, "displayTransform.name", "plugValueWidget:type", "GafferImageUI.OpenColorIOConfigPlugUI.DisplayTransformPlugValueWidget" )
Gaffer.Metadata.registerValue( GafferUI.View, "displayTransform.name", "layout:minimumWidth", 150 )
Gaffer.Metadata.registerValue( GafferUI.View, "displayTransform.name", "userDefault", "__default__" )

# Add "Roles" submenus to various colorspace plugs. The OCIO UX guidelines suggest we
# shouldn't do this, but they do seem like they might be useful, and historically they
Expand Down
Loading