0

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 ?

quellenform
  • 35,177
  • 10
  • 50
  • 133
Jean203
  • 1
  • 1

1 Answers1

4

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

pyCod3R
  • 1,926
  • 4
  • 15