Layer blending mode editor
This example shows how to gather in a dock the list of all the channels of the currently selected layer to be able to change their blending mode.
Note: to keep things simple, this example ignore effects.
To use this example:
- Go to your Python plugin folder (see this page to know where)
- Create a folder named blending_mode
- Create a file named init.py
- Copy the code below inside the file.
Copied to your clipboardimport osimport substance_painter as sp# Qt5 vs Qt6 checkIsQt5 = sp.application.version_info() < (10,1,0)if IsQt5 :from PySide2 import QtGuifrom PySide2 import QtWidgetselse :from PySide6 import QtGuifrom PySide6 import QtWidgetsSCRIPT_FOLDER = os.path.dirname(os.path.realpath(__file__))WIDGETS = []WIDGET_NODE_NAME = NoneWIDGET_NODE_TYPE = NoneWIDGET_NODE_ID = NoneWIDGET_BLEND_LAYOUT = NoneVALID_TYPES = [sp.layerstack.NodeType.PaintLayer,sp.layerstack.NodeType.FillLayer]FRAME_STYLE = """.QFrame{border: 1px solid #252525;border-radius: 4px;background-color: #303030;}"""BLEND_MODES = []def ChangeBlendMode( Layer, ChannelType, Dropdown ) :# Retrieve blending modeIndex = Dropdown.currentIndex()BlendMode = sp.layerstack.BlendingMode( Index )# Change blending modeLayer.set_blending_mode( BlendMode, ChannelType )def BuildChannelUI( Layer, ChannelType, ChannelInfo ) :Parent = QtWidgets.QFrame()Parent.setFrameStyle( QtWidgets.QFrame.Panel )Parent.setFrameShadow( QtWidgets.QFrame.Raised )Parent.setStyleSheet( FRAME_STYLE )Layout = QtWidgets.QVBoxLayout()Parent.setLayout( Layout )Label = QtWidgets.QLabel( ChannelType.name )Layout.addWidget( Label )Dropdown = QtWidgets.QComboBox()Dropdown.addItems( BLEND_MODES )Layout.addWidget( Dropdown )# Retrieve layer current blending mode# and update dropdown to show itBlendModeName = Layer.get_blending_mode( ChannelType ).nameBlendModeIndex = BLEND_MODES.index( BlendModeName )Dropdown.setCurrentIndex( BlendModeIndex )# Connect a function to be able to change the# blending mode from our own UIDropdown.currentIndexChanged.connect(lambda: ChangeBlendMode( Layer, ChannelType, Dropdown ))WIDGET_BLEND_LAYOUT.addWidget(Parent)def Update( Arg ) :global WIDGET_NODE_IDStack = sp.textureset.get_active_stack()Layers = sp.layerstack.get_selected_nodes( Stack )if not len(Layers) == 1 :WIDGET_NODE_NAME.setText( "(Too many selected)" )WIDGET_NODE_TYPE.setText( "(none)" )WIDGET_BLEND_LAYOUT.setEnabled( False )ResetUI()returnif not any( Layers[0].get_type() == Type for Type in VALID_TYPES ) :WIDGET_NODE_NAME.setText( "(Unsupported layer/effect)" )WIDGET_NODE_TYPE.setText( "(none)" )WIDGET_BLEND_LAYOUT.setEnabled( False )ResetUI()returnWIDGET_NODE_NAME.setText( Layers[0].get_name() )WIDGET_NODE_TYPE.setText( "(" + str(Layers[0].get_type()) + ")" )WIDGET_BLEND_LAYOUT.setEnabled( True )ResetUI()Channels = Stack.all_channels()for ChannelType in Channels :BuildChannelUI( Layers[0], ChannelType, Channels[ChannelType] )def ResetUI() :if WIDGET_BLEND_LAYOUT :while WIDGET_BLEND_LAYOUT.count():child = WIDGET_BLEND_LAYOUT.takeAt(0)if child.widget():child.widget().deleteLater()def Separator() :Separator = QtWidgets.QFrame()Separator.setFrameShape( QtWidgets.QFrame.HLine )Separator.setFrameShadow( QtWidgets.QFrame.Raised )Separator.setStyleSheet( "QFrame { border-bottom: 1px solid #252525; }" )return Separatordef BuildUI() :global WIDGET_NODE_NAMEglobal WIDGET_NODE_TYPEglobal WIDGET_BLEND_LAYOUTParent = QtWidgets.QFrame()Parent.setWindowTitle("Blend Modes")Parent.setWindowIcon( QtGui.QIcon(SCRIPT_FOLDER + "/" + "icon.svg") )ParentLayout = QtWidgets.QVBoxLayout()Parent.setLayout( ParentLayout )Margin = 10ParentLayout.setContentsMargins( Margin, Margin, Margin, Margin )# Build top UITitleLabel = QtWidgets.QLabel( "<b>INFO</b>" )NodeLabel = QtWidgets.QLabel( "(No layer selected)" )NodeType = QtWidgets.QLabel( "(none)" )ParentLayout.addWidget( TitleLabel )ParentLayout.addWidget( NodeLabel )ParentLayout.addWidget( NodeType )EmptyLabel = QtWidgets.QLabel( "" )ParentLayout.addWidget( EmptyLabel )ParentLayout.addWidget( Separator() )# Build scrollable layout for blend modesBlendLabel = QtWidgets.QLabel( "<b>BLEND MODES</b>" )ParentLayout.addWidget( BlendLabel )Widget = QtWidgets.QWidget()VerticalLayout = QtWidgets.QVBoxLayout( Widget )BlendLayout = QtWidgets.QVBoxLayout()VerticalLayout.addLayout( BlendLayout )VerticalLayout.addStretch()VerticalLayout.setContentsMargins( 0, 0, 8, 0 )ScrollArea = QtWidgets.QScrollArea()ScrollArea.setWidget( Widget )ScrollArea.setWidgetResizable( True )ParentLayout.addWidget( ScrollArea )# FinishWIDGET_NODE_NAME = NodeLabelWIDGET_NODE_TYPE = NodeTypeWIDGET_BLEND_LAYOUT = BlendLayoutreturn Parentdef start_plugin():global BLEND_MODES# Add event for getting layer stack selection updatessp.event.DISPATCHER.connect( sp.event.LayerStacksModelDataChanged, Update )# Build dock UIParent = BuildUI()sp.ui.add_dock_widget( Parent, sp.ui.UIMode.Edition )ResetUI()WIDGETS.append(Parent)# Some enums at not iteratable currently,# so we rely on the members attribute to build a listBLEND_MODES = [ Value for Value in sp.layerstack.BlendingMode.__members__ ]def close_plugin():sp.event.DISPATCHER.disconnect( sp.event.LayerStacksModelDataChanged, Update )for Widget in WIDGETS :sp.ui.delete_ui_element( Widget )WIDGETS.clear()if __name__ == "__main__":start_plugin()