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" />
+
+