Skip to content

Commit

Permalink
Merge pull request #6122 from murraystevenson/readOnlyEditScopeIndicator
Browse files Browse the repository at this point in the history
EditScopePlugValueWidget : Display lock icon next to read-only nodes
  • Loading branch information
johnhaddon authored Nov 20, 2024
2 parents 0e5e2d6 + e956752 commit 5aa13b8
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 10 deletions.
2 changes: 2 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Improvements
- The "Source" menu item now displays a checkbox when chosen.
- Added a "No EditScopes Available" menu item that is displayed when no upstream EditScopes are available.
- Increased menu item icon sizes.
- A lock icon is now displayed next to read-only nodes.

Fixes
-----
Expand All @@ -51,6 +52,7 @@ API
- Added `connectToApplication()` function.
- Deprecated `connect()` function. Use `connectToApplication()` instead.
- SceneEditor : Added `editScope()` method.
- Image : Added optional `image` argument to `createSwatch()` static method.

1.5.0.1 (relative to 1.5.0.0)
=======
Expand Down
33 changes: 25 additions & 8 deletions python/GafferUI/EditScopeUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ def __init__( self, plug, **kw ) :
# run the default dropSignal handler from PlugValueWidget.
self.dropSignal().connectFront( Gaffer.WeakMethod( self.__drop ) )

self.__nodeMetadataChangedConnection = Gaffer.Metadata.nodeValueChangedSignal().connect(
Gaffer.WeakMethod( self.__nodeMetadataChanged ), scoped = True
)

self.__updateLabelVisibility()
self.__updatePlugInputChangedConnection()
self.__acquireContextTracker()
Expand All @@ -165,8 +169,11 @@ def getToolTip( self ) :
return "Edits will be made using the last relevant node found outside of an edit scope.\n\nTo make an edit in an edit scope, choose it from the menu."

unusableReason = self.__unusableReason( editScope )
readOnlyReason = self.__readOnlyReason( editScope )
if unusableReason :
return unusableReason
elif readOnlyReason :
return readOnlyReason
else :
return "Edits will be made in {}.".format( editScope.getName() )

Expand All @@ -185,12 +192,8 @@ def _updateFromValues( self, values, exception ) :
self.__editScopeNameChangedConnection = editScope.nameChangedSignal().connect(
Gaffer.WeakMethod( self.__editScopeNameChanged ), scoped = True
)
self.__editScopeMetadataChangedConnection = Gaffer.Metadata.nodeValueChangedSignal( editScope ).connect(
Gaffer.WeakMethod( self.__editScopeMetadataChanged ), scoped = True
)
else :
self.__editScopeNameChangedConnection = None
self.__editScopeMetadataChangedConnection = None

def __updatePlugInputChangedConnection( self ) :

Expand Down Expand Up @@ -276,9 +279,13 @@ def __editScopeNameChanged( self, editScope, oldName ) :

self.__updateMenuButton()

def __editScopeMetadataChanged( self, editScope, key, reason ) :
def __nodeMetadataChanged( self, nodeTypeId, key, node ) :

if key == "nodeGadget:color" :
editScope = self.__editScope()
if (
Gaffer.MetadataAlgo.readOnlyAffectedByChange( editScope, nodeTypeId, key, node ) or
node == editScope and key == "nodeGadget:color"
) :
self.__updateMenuButton()

def __contextTrackerChanged( self, contextTracker ) :
Expand Down Expand Up @@ -393,7 +400,7 @@ def __buildMenu( self, path, currentEditScope ) :
"checkBox" : editScope == currentEditScope,
"icon" : self.__editScopeSwatch( editScope ),
"active" : not self.__unusableReason( editScope ),
"description" : self.__unusableReason( editScope ),
"description" : self.__unusableReason( editScope ) or self.__readOnlyReason( editScope ),
}
)
else :
Expand Down Expand Up @@ -498,7 +505,8 @@ def __refreshMenu( self ) :
def __editScopeSwatch( self, editScope ) :

return GafferUI.Image.createSwatch(
Gaffer.Metadata.value( editScope, "nodeGadget:color" ) or imath.Color3f( 1 )
Gaffer.Metadata.value( editScope, "nodeGadget:color" ) or imath.Color3f( 1 ),
image = "menuLock.png" if Gaffer.MetadataAlgo.readOnly( editScope ) else None
)

@staticmethod
Expand Down Expand Up @@ -590,6 +598,15 @@ def __unusableReason( self, editScope ) :
else :
return None

def __readOnlyReason( self, editScope ) :

if Gaffer.MetadataAlgo.readOnly( editScope ) :
return "{} is locked.".format(
Gaffer.MetadataAlgo.readOnlyReason( editScope ).relativeName( editScope.scriptNode() )
)

return None

# ProcessorWidget
# ===============

Expand Down
31 changes: 29 additions & 2 deletions python/GafferUI/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ def __init__( self, imagePrimitiveOrFileName, **kw ) :
self.__pixmapDisabled = None

## Creates an Image containing a color swatch useful for
# button and menu icons.
# button and menu icons. An `image` can be overlaid on
# top of the swatch, this will be scaled to fit.
@staticmethod
def createSwatch( color ) :
def createSwatch( color, image = None ) :

pixmap = QtGui.QPixmap( 14, 14 )
pixmap.fill( QtGui.QColor( 0, 0, 0, 0 ) )
Expand All @@ -84,6 +85,32 @@ def createSwatch( color ) :
painter.setPen( GafferUI._StyleSheet.styleColor( "backgroundDarkHighlight" ) )
painter.setBrush( QtGui.QColor.fromRgbF( color[0], color[1], color[2] ) )
painter.drawRoundedRect( QtCore.QRectF( 0.5, 0.5, 13, 13 ), 2, 2 )

if image is not None :

try :
iconImage = GafferUI.Image( image )
except Exception as e:
IECore.msg( IECore.Msg.Level.Error, "GafferUI.Image",
"Could not read image for swatch icon : " + str( e )
)
iconImage = GafferUI.Image( "warningSmall.png" )

iconSize = pixmap.size() - QtCore.QSize( 2, 2 )
iconPixmap = iconImage._qtPixmap()
if iconPixmap.width() > iconSize.width() or iconPixmap.height() > iconSize.height() :
iconPixmap = iconPixmap.scaled(
iconSize,
QtCore.Qt.AspectRatioMode.KeepAspectRatio,
QtCore.Qt.TransformationMode.SmoothTransformation
)

painter.drawPixmap(
( pixmap.width() - iconPixmap.width() ) // 2,
( pixmap.height() - iconPixmap.height() ) // 2,
iconPixmap
)

del painter

swatch = GafferUI.Image( None )
Expand Down
21 changes: 21 additions & 0 deletions python/GafferUITest/ImageTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

import imath

import IECore

import GafferUI
import GafferUITest

Expand Down Expand Up @@ -73,5 +75,24 @@ def testCreateSwatch( self ) :
self.assertEqual( s._qtPixmap().width(), 14 )
self.assertEqual( s._qtPixmap().height(), 14 )

def testCreateSwatchWithImage( self ) :

s = GafferUI.Image.createSwatch( imath.Color3f( 1, 0, 0 ), image = "arrowRight10.png" )

self.assertEqual( s._qtPixmap().width(), 14 )
self.assertEqual( s._qtPixmap().height(), 14 )

# Create a swatch with a large image. The swatch size should not increase.
s2 = GafferUI.Image.createSwatch( imath.Color3f( 1, 0, 0 ), image = "warningNotification.png" )

self.assertEqual( s2._qtPixmap().width(), 14 )
self.assertEqual( s2._qtPixmap().height(), 14 )

with IECore.CapturingMessageHandler() as mh :
GafferUI.Image.createSwatch( imath.Color3f( 1, 0, 0 ), image = "iAmNotAFile" )

self.assertEqual( len( mh.messages ), 1 )
self.assertIn( 'Unable to find file "iAmNotAFile"', mh.messages[0].message )

if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions resources/graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@
"menuIndicator",
"menuIndicatorDisabled",
"menuSource",
"menuLock",
]
},

Expand Down
15 changes: 15 additions & 0 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.

0 comments on commit 5aa13b8

Please sign in to comment.