Skip to content

Commit

Permalink
ColorChooser : Maintain options state per-session
Browse files Browse the repository at this point in the history
  • Loading branch information
ericmehl committed Aug 19, 2024
1 parent ebeee1f commit f3c8804
Show file tree
Hide file tree
Showing 5 changed files with 457 additions and 13 deletions.
6 changes: 5 additions & 1 deletion Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ Features
- ColorChooser :
- Add sliders for TMI (temperature, magenta, intensity) color space.
- Add color field, a widget giving control of two channels of "RGB", "HSV" or "TMI" triplets. The third channel is held constant.
- The color field, HSV slider group and TMI slider group can be toggled on or off.
- The color field, HSV slider group and TMI slider group can be toggled on or off. Visibility of the elements is maintained for the duration of the Gaffer session.
- Default visibility of the UI elements can be set at startup by setting `colorChooser:inlineOptions` and `colorChooser:dialogueOptions` `userData` entries for the inline chooser and dialogue chooser respectively. The value of the metadata entry is an `IECore.CompoundData` object with the following optional keys :
- `visibleComponents` : A string where each character is a visible component slider. Optional components are `rgbahsvtmi`.
- `staticComponent` : A single character string for the component to use as the static component for the color field.
- `colorFieldVisible` : A boolean indicating if the color field should be visible or not.

1.4.11.0 (relative to 1.4.10.0)
========
Expand Down
112 changes: 100 additions & 12 deletions python/GafferUI/ColorChooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ def __init__( self, color=imath.Color3f( 1 ), **kw ) :
self.__channelLabels[component]._qtWidget().setProperty( "gafferColorStaticComponent", True)
self.__channelLabels[component].buttonPressSignal().connect(
functools.partial(
Gaffer.WeakMethod( self.__setStaticComponent ),
Gaffer.WeakMethod( self.__channelLabelPressed ),
component = component
),
scoped = False
Expand Down Expand Up @@ -572,6 +572,9 @@ def __init__( self, color=imath.Color3f( 1 ), **kw ) :
self.__colorSwatch._qtWidget().setFixedHeight( 40 )

self.__colorChangedSignal = Gaffer.Signals.Signal2()
self.__visibleComponentsChangedSignal = Gaffer.Signals.Signal2()
self.__staticComponentChangedSignal = Gaffer.Signals.Signal2()
self.__colorFieldVisibleChangedSignal = Gaffer.Signals.Signal2()

self.__updateUIFromColor()
self.__activateComponentIcons()
Expand Down Expand Up @@ -611,6 +614,31 @@ def setErrored( self, errored ) :
def getErrored( self ) :
return any( w.getErrored() for w in self.__numericWidgets.values() )

def setVisibleComponents( self, components ) :

self.__setVisibleComponentsInternal( components )

def getVisibleComponents( self ) :

return "".join( [ k for k, v in self.__sliders.items() if v.getVisible() ] )

def setStaticComponent( self, component ) :

self.__setStaticComponentInternal( component )

def getStaticComponent( self ) :

c, staticComponent = self.__colorField.getColor()
return staticComponent

def setColorFieldVisible( self, visible ) :

self.__setColorFieldVisibleInternal( visible )

def getColorFieldVisible( self ) :

return self.__colorField.getVisible()

## A signal emitted whenever the color is changed. Slots should
# have the signature slot( ColorChooser, reason ). The reason
# argument may be passed either a ColorChooser.ColorChangedReason,
Expand All @@ -620,6 +648,29 @@ def colorChangedSignal( self ) :

return self.__colorChangedSignal

## A signal emitted whenver the visible components are changed. Slots
# should have the signature slot( ColorChooser, visibleComponents ).
# `visibleComponents` is a string representing the components currently
# visible.
def visibleComponentsChangedSignal( self ) :

return self.__visibleComponentsChangedSignal

## A signal emitted whenver the static component is changed. Slots
# should have the signature slot( ColorChooser, staticComponent ).
# `staticComponent` is a single character string representing the
# current static component.
def staticComponentChangedSignal( self ) :

return self.__staticComponentChangedSignal

## A signal emitted whenever the visibility of the color field changes.
# Slots should have the signature slot( ColorChooser, visible ).
# `visible` is a boolean representing the current visibility.
def colorFieldVisibleChangedSignal( self ) :

return self.__colorFieldVisibleChangedSignal

## Returns True if a user would expect the specified sequence
# of changes to be merged into a single undoable event.
@classmethod
Expand Down Expand Up @@ -681,22 +732,29 @@ def __optionsMenuDefinition( self ) :

return result

def __channelLabelPressed( self, widget, event, component ) :

if event.buttons != GafferUI.ButtonEvent.Buttons.Left :
return False

self.__setStaticComponentInternal( component )

return True

def __toggleComponentTriplet( self, channels, *unused ) :

visibleComponents = set( self.getVisibleComponents() )
for c in channels :
self.__channelLabels[c].setVisible( not self.__channelLabels[c].getVisible() )
self.__numericWidgets[c].setVisible( not self.__numericWidgets[c].getVisible() )
self.__sliders[c].setVisible( not self.__sliders[c].getVisible() )
if c in visibleComponents :
visibleComponents.remove( c )
else :
visibleComponents.add( c )

self.__setVisibleComponentsInternal( "".join( visibleComponents ) )

def __toggleColorField( self, *unused ) :

visible = not self.__colorField.getVisible()
self.__colorField.setVisible( visible )

if visible :
self.__activateComponentIcons()
else :
self.__clearComponentIcons()
self.__setColorFieldVisibleInternal( not self.__colorField.getVisible() )

def __initialColorPress( self, button, event ) :

Expand Down Expand Up @@ -939,4 +997,34 @@ def __setStaticComponentInternal( self, component ) :
self.__colorField.setColor( self.__colorTMI, component )

if self.__colorField.getVisible() :
self.__activateComponentIcons()
self.__activateComponentIcons()

self.__staticComponentChangedSignal( self, component )

def __setVisibleComponentsInternal( self, components ) :

componentsSet = set( components )
if componentsSet == set( self.getVisibleComponents() ) :
return

for c in self.__sliders.keys() :
visible = c in componentsSet
self.__channelLabels[c].setVisible( visible )
self.__numericWidgets[c].setVisible( visible )
self.__sliders[c].setVisible( visible )

self.__visibleComponentsChangedSignal( self, components )

def __setColorFieldVisibleInternal( self, visible ) :

if visible == self.__colorField.getVisible() :
return

self.__colorField.setVisible( visible )

if visible :
self.__activateComponentIcons()
else :
self.__clearComponentIcons()

self.__colorFieldVisibleChangedSignal( self, visible )
65 changes: 65 additions & 0 deletions python/GafferUI/ColorChooserPlugValueWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@
#
##########################################################################

import functools
import imath

import IECore

import Gaffer
import GafferUI
from GafferUI.PlugValueWidget import sole

class ColorChooserPlugValueWidget( GafferUI.PlugValueWidget ) :

Expand All @@ -49,10 +53,43 @@ def __init__( self, plugs, **kw ) :

self.__colorChooser.setSwatchesVisible( False )

options = self.__colorChooserOptions()

if "visibleComponents" in options :
self.__colorChooser.setVisibleComponents( options["visibleComponents"].value )

if "staticComponent" in options :
self.__colorChooser.setStaticComponent( options["staticComponent"].value )

if "colorFieldVisible" in options :
self.__colorChooser.setColorFieldVisible( options["colorFieldVisible"].value )

self.__colorChangedConnection = self.__colorChooser.colorChangedSignal().connect(
Gaffer.WeakMethod( self.__colorChanged ), scoped = False
)

self.__colorChooser.visibleComponentsChangedSignal().connect(
functools.partial(
Gaffer.WeakMethod( self.__colorChooserOptionChanged ),
key = "visibleComponents"
),
scoped = False
)
self.__colorChooser.staticComponentChangedSignal().connect(
functools.partial(
Gaffer.WeakMethod( self.__colorChooserOptionChanged ),
key = "staticComponent"
),
scoped = False
)
self.__colorChooser.colorFieldVisibleChangedSignal().connect(
functools.partial(
Gaffer.WeakMethod( self.__colorChooserOptionChanged ),
key = "colorFieldVisible"
),
scoped = False
)

self.__lastChangedReason = None
self.__mergeGroupId = 0

Expand Down Expand Up @@ -89,6 +126,34 @@ def __colorChanged( self, colorChooser, reason ) :
for plug in self.getPlugs() :
plug.setValue( self.__colorChooser.getColor() )

def __colorChooserOptionChanged( self, colorChooser, value, key ) :

if Gaffer.Metadata.value( "colorChooser:inlineOptions", "userDefault" ) is None :
sessionOptions = Gaffer.Metadata.value( "colorChooser:inlineOptions", "sessionDefault" )
if sessionOptions is None :
sessionOptions = IECore.CompoundData()
Gaffer.Metadata.registerValue( "colorChooser:inlineOptions", "sessionDefault", sessionOptions )

sessionOptions.update( { key: value } )

for p in self.getPlugs() :
plugOptions = Gaffer.Metadata.value( p, "colorChooser:inlineOptions" )
if plugOptions is None :
plugOptions = IECore.CompoundData()
Gaffer.Metadata.registerValue( p, "colorChooser:inlineOptions", plugOptions, persistent = False )

plugOptions.update( { key: value } )

def __colorChooserOptions( self ) :

v = sole( Gaffer.Metadata.value( p, "colorChooser:inlineOptions" ) for p in self.getPlugs() )
if v is None :
v = Gaffer.Metadata.value( "colorChooser:inlineOptions", "userDefault" )
if v is None :
v = Gaffer.Metadata.value( "colorChooser:inlineOptions", "sessionDefault" ) or IECore.CompoundData()

return v

def __allComponentsEditable( self ) :

if not self._editable() :
Expand Down
69 changes: 69 additions & 0 deletions python/GafferUI/ColorSwatchPlugValueWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,13 @@

import weakref
import imath
import functools

import IECore

import Gaffer
import GafferUI
from GafferUI.PlugValueWidget import sole

class ColorSwatchPlugValueWidget( GafferUI.PlugValueWidget ) :

Expand Down Expand Up @@ -132,6 +136,29 @@ def __init__( self, plugs, parentWindow ) :
self.__mergeGroupId = 0

self.__colorChangedConnection = self.colorChooser().colorChangedSignal().connect( Gaffer.WeakMethod( self.__colorChanged ), scoped = False )

self.colorChooser().visibleComponentsChangedSignal().connect(
functools.partial(
Gaffer.WeakMethod( self.__colorChooserOptionChanged ),
key = "visibleComponents"
),
scoped = False
)
self.colorChooser().staticComponentChangedSignal().connect(
functools.partial(
Gaffer.WeakMethod( self.__colorChooserOptionChanged ),
key = "staticComponent"
),
scoped = False
)
self.colorChooser().colorFieldVisibleChangedSignal().connect(
functools.partial(
Gaffer.WeakMethod( self.__colorChooserOptionChanged ),
key = "colorFieldVisible"
),
scoped = False
)

self.confirmButton.clickedSignal().connect( Gaffer.WeakMethod( self.__buttonClicked ), scoped = False )
self.cancelButton.clickedSignal().connect( Gaffer.WeakMethod( self.__buttonClicked ), scoped = False )

Expand All @@ -151,6 +178,17 @@ def __init__( self, plugs, parentWindow ) :

self.__plugSet( plug )

options = self.__colorChooserOptions()

if "visibleComponents" in options :
self.colorChooser().setVisibleComponents( options["visibleComponents"].value )

if "staticComponent" in options :
self.colorChooser().setStaticComponent( options["staticComponent"].value )

if "colorFieldVisible" in options :
self.colorChooser().setColorFieldVisible( options["colorFieldVisible"].value )

parentWindow.addChildWindow( self, removeOnClose = True )

@classmethod
Expand Down Expand Up @@ -214,3 +252,34 @@ def __buttonClicked( self, button ) :
def __destroy( self, *unused ) :

self.parent().removeChild( self )

# \todo Extract these two methods to share with `ColorChooserPlugValueWidget` which has
# an almost identical implementation.

def __colorChooserOptionChanged( self, colorChooser, value, key ) :

if Gaffer.Metadata.value( "colorChooser:dialogueOptions", "userDefault" ) is None :
sessionOptions = Gaffer.Metadata.value( "colorChooser:dialogueOptions", "sessionDefault" )
if sessionOptions is None :
sessionOptions = IECore.CompoundData()
Gaffer.Metadata.registerValue( "colorChooser:dialogueOptions", "sessionDefault", sessionOptions )

sessionOptions.update( { key: value } )

for p in self.__plugs :
plugOptions = Gaffer.Metadata.value( p, "colorChooser:dialogueOptions" )
if plugOptions is None :
plugOptions = IECore.CompoundData()
Gaffer.Metadata.registerValue( p, "colorChooser:dialogueOptions", plugOptions, persistent = False )

plugOptions.update( { key: value } )

def __colorChooserOptions( self ) :

v = sole( Gaffer.Metadata.value( p, "colorChooser:dialogueOptions" ) for p in self.__plugs )
if v is None :
v = Gaffer.Metadata.value( "colorChooser:dialogueOptions", "userDefault" )
if v is None :
v = Gaffer.Metadata.value( "colorChooser:dialogueOptions", "sessionDefault" ) or IECore.CompoundData()

return v
Loading

0 comments on commit f3c8804

Please sign in to comment.