From 28871619e5f5e3a1a4af97fef61e4ed84094890a Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:21:41 +1100 Subject: [PATCH 1/2] Image : Allow swatches to be created with an icon overlaid --- Changes.md | 1 + python/GafferUI/Image.py | 31 +++++++++++++++++++++++++++++-- python/GafferUITest/ImageTest.py | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Changes.md b/Changes.md index accb3e4a3b..d2b40218fb 100644 --- a/Changes.md +++ b/Changes.md @@ -51,6 +51,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) ======= diff --git a/python/GafferUI/Image.py b/python/GafferUI/Image.py index a70c8353e4..d6ad7d18f6 100644 --- a/python/GafferUI/Image.py +++ b/python/GafferUI/Image.py @@ -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 ) ) @@ -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 ) diff --git a/python/GafferUITest/ImageTest.py b/python/GafferUITest/ImageTest.py index ee821a3646..d7c14be1cc 100644 --- a/python/GafferUITest/ImageTest.py +++ b/python/GafferUITest/ImageTest.py @@ -39,6 +39,8 @@ import imath +import IECore + import GafferUI import GafferUITest @@ -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() From e95675209dfd4eddd822082242691e31ed0da8dd Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:55:07 +1100 Subject: [PATCH 2/2] EditScopePlugValueWidget : Add lock icon to read-only EditScopes --- Changes.md | 1 + python/GafferUI/EditScopeUI.py | 33 +++++++++++++++++++++++++-------- resources/graphics.py | 1 + resources/graphics.svg | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Changes.md b/Changes.md index d2b40218fb..ae92c966a4 100644 --- a/Changes.md +++ b/Changes.md @@ -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 ----- diff --git a/python/GafferUI/EditScopeUI.py b/python/GafferUI/EditScopeUI.py index a1820bc069..256160b29c 100644 --- a/python/GafferUI/EditScopeUI.py +++ b/python/GafferUI/EditScopeUI.py @@ -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() @@ -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() ) @@ -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 ) : @@ -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 ) : @@ -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 : @@ -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 @@ -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 # =============== diff --git a/resources/graphics.py b/resources/graphics.py index 5f065971bf..b3345204c5 100644 --- a/resources/graphics.py +++ b/resources/graphics.py @@ -414,6 +414,7 @@ "menuIndicator", "menuIndicatorDisabled", "menuSource", + "menuLock", ] }, diff --git a/resources/graphics.svg b/resources/graphics.svg index 15bd6b37f6..10903ecaa3 100644 --- a/resources/graphics.svg +++ b/resources/graphics.svg @@ -3466,6 +3466,15 @@ y="95" transform="matrix(0,1,1,0,0,0)" inkscape:label="menuSource" /> + +