0
$\begingroup$

Here is my problem : i would like to store color data in my blender files and be able to switch between all colors for my object with a python script. So i tried to create custom properties for every color, with a "main property" which is linked to my materials. And then i would like to create a script to copy the RGB values of the stored colors and paste those values in my "Main Property".

The goal is to have a system that can be used for any type of object, no matter how much materials or colors it has : just change the "color" property and it will change every material using this color.

But is it possible ? And how can i write it in script ?

EDIT : In fact, I just want a way to change the base color of several materials with a python script. So if there is a simpler way to do so... XD


Thanks for your answers !

I finally successed to create a python script that works. The simplest way, maybe not the best. I created a "Main Color" property and a bunch of color properties and scripted it like this :

bpy.data.objects["MonObjet"]["MainColor"][0]=bpy.data.objects["MonObjet"]["9440"][0]
bpy.data.objects["MonObjet"]["MainColor"][1]=bpy.data.objects["MonObjet"]["9440"][1]
bpy.data.objects["MonObjet"]["MainColor"][2]=bpy.data.objects["MonObjet"]["9440"][2]
bpy.data.objects["MonObjet"]["MainColor"][3]=bpy.data.objects["MonObjet"]["9440"][3]

"9440" is the color reference, and MainColor is the driver pasted on all my materials. So my script just copy/paste the values of the stored color on the "MainColor" Property.

What do you think, is there a better way to do ?

$\endgroup$

1 Answer 1

3
$\begingroup$

You can use series of bool properties (as elements of a collection) which allows to have an active item and also trigger an event by using the update function of each BoolProperty.

Each BoolProperty can be displayed as an icon and the icon itself can be generated on the fly by adding a new preview collection and then assigning a pixel array to each new icon added: https://docs.blender.org/api/current/bpy.types.ImagePreview.html

enter image description here

Example of a custom color palette using BoolProperties. When a color is selected from the list, the color is assigned to base color of the principled shader and the diffuse color of all materials.

import bpy
import bpy.utils.previews

class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout
        scn = context.scene
        
        col = layout.column(align=True)
        row = col.row(align=True)
        active_item = None
        for idx, item in enumerate(scn.color_collection, start=1):
            row.prop(item, "active", icon_value=item.icon, icon_only=True)
            if item.active == True:
                active_item = item
            if idx % 11 == 0:
                row = col.row(align=True)
            
        if active_item:
            row = layout.row()
            r, g, b, a = active_item.color
            row.label(text=f"Active item: {r:.2f} {g:.2f} {b:.2f} {a:.2f}")                


def update_callback(self, context):
    if self.active:
        for i in self.id_data.color_collection:
            if i.name != self.name:
                i.active = False
    
    # set the diffuse and base color for all materials
    for slot in context.object.material_slots:
        material = slot.material
        material.diffuse_color = self.color
        if material.use_nodes:
            for node in material.node_tree.nodes:
                if node.type == 'BSDF_PRINCIPLED':
                    node.inputs['Base Color'].default_value = self.color

class ColorCollection(bpy.types.PropertyGroup):
    # name: bpy.props.StringProperty
    active: bpy.props.BoolProperty(default=False, update=update_callback)
    icon: bpy.props.IntProperty()
    color: bpy.props.FloatVectorProperty(
         name = "Color",
         subtype = "COLOR",
         default = (1.0,1.0,1.0,1.0),
         size = 4)

# We can store multiple preview collections here,
# however in this example we only store "main"
preview_collections = {}

color_palette = [
    (0.46, 0.41, 0.62, 1),
    (0.67, 0.18, 0.34, 1),
    (0.64, 0.10, 0.91, 1),
    (0.66, 0.09, 0.01, 1),
    (0.86, 0.90, 0.90, 1),
    (0.95, 0.27, 0.83, 1),
    (0.53, 0.08, 0.78, 1),
    (0.47, 0.27, 0.61, 1)]


def register():
    
    # register the classes
    bpy.utils.register_class(HelloWorldPanel)
    bpy.utils.register_class(ColorCollection)
    bpy.types.Scene.color_collection = bpy.props.CollectionProperty(type=ColorCollection)
    
    # clear the collection
    if hasattr(bpy.context.scene, "color_collection"):
        bpy.context.scene.color_collection.clear()
        
    # generate colors and icons
    pcoll = bpy.utils.previews.new()
    
    size = 32, 32
    for i, color in enumerate(color_palette):
        
        color_name = f"Color{i}"
        pixels = [*color] * size[0] * size[1]
        icon = pcoll.new(color_name) # name has to be unique!
        icon.icon_size = size
        icon.is_icon_custom = True
        icon.icon_pixels_float = pixels
        
        # add the item to the collection
        color_item = bpy.context.scene.color_collection.add()
        color_item.name = color_name
        color_item.color = color
        color_item.icon = pcoll[color_name].icon_id
        
    preview_collections["main"] = pcoll
    

def unregister():
    for pcoll in preview_collections.values():
        bpy.utils.previews.remove(pcoll)
    preview_collections.clear()
    
    bpy.utils.unregister_class(ColorCollection)
    bpy.utils.unregister_class(HelloWorldPanel)
    
    del bpy.types.Scene.color_collection


if __name__ == "__main__":
    register()

If you prefer to use an operator instead of the update function, see one of my previous answers to: How to display a fixed list of RGB values in a panel and when a color is clicked call an operator

$\endgroup$
1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .