0
$\begingroup$

I make that script (Copy a piece from template UIList) Now i can draw My box with inside the active object material but i want to populate that with all material in scene, i not undertand , how to. Part of my script:

class MATERIAL_UL_extreme_matslot(bpy.types.UIList):

    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        ob = data
        slot = item
        ma = slot.material

        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            if ma:               
                layout.prop(ma, "name", text= "", emboss=False, icon_value=icon)
            else:               
                layout.label(text="", translate=False, icon_value=icon)
                elif self.layout_type in {'GRID'}:

                    layout.alignment = 'CENTER'
                    layout.label(text="", icon_value=icon)

Then for draw in panel:

class MY_PT_Panel(bpy.types.Panel):

    bl_label = "My label"
    bl_idname = "MY_ID_NAME"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "My Category"
    def draw(self, context):

    def draw(self, context):

        obj = context.object  
        layout = self.layout
        row = layout.row()
        row.template_list("MATERIAL_UL_extreme_matslot", "", obj, "material_slots", obj, "active_material_index")

Im not sure if that is the best solution because i want to select material slot from that UIList panel.

$\endgroup$

1 Answer 1

5
$\begingroup$

Scene.material_slots

enter image description here

If I understand question you want to display all the materials used by all the objects in the scene. Every bpy.types.Object has a material slots collection, a scene does not.

Added a material slots collection to the scene. Used an old fashioned python property to populate Scene.materials from the non None materials on the objects in the scene.

def get_scene_materials(self):    
    return set(s.material for o in self.objects 
            for s in o.material_slots if s.material
            )
bpy.types.Scene.materials = property(get_scene_materials)

To use a UIList AFAIK need the list to be a collection, the index to be an int property.

The scene material slots is not populated automatically. If the set of materials in scene, does not match those in material slots a toggle button appears to repopulate the slots. There are a couple of ways to make this automatic, but AFAIC are not worth the overhead.

The property Scene.materials will always return a set of all materials on all objects in scene.

bl_info = {
    "name": "Scene Materials",
    "author": "batFINGER",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > UI > Scene",
    "description": "List All Materials in Scene",
    "warning": "",
    "doc_url": "https://blender.stackexchange.com/a/141207/15543",
    "category": "Materials",
}

import bpy
from bpy.types import PropertyGroup
from bpy.props import (
    CollectionProperty,
    IntProperty,
    BoolProperty,
    StringProperty,
    PointerProperty,
)

def get_update_materials(self):

    update = set(s.material for s in self.material_slots).symmetric_difference(self.materials)
    if update:
        set_update_materials(self, True)
    return False


def set_update_materials(self, value):
    if value:
        self.material_slots.clear() # update (or get) instead?
        for m in self.materials:
            s = self.material_slots.add()
            s.name = m.name
            s.material = m


def get_scene_materials(self):
    return set(s.material for o in self.objects
               for s in o.material_slots if s.material)


class SceneMaterialSlot(PropertyGroup):
    def get_name(self):
        return getattr(self.material, "name", "")
    material: PointerProperty(type=bpy.types.Material)
    #name : StringProperty(get=get_name)


class MATERIAL_UL_extreme_matslot(bpy.types.UIList):

    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        ob = data
        slot = item
        ma = slot.material

        if self.layout_type in {'DEFAULT', 'COMPACT'}:
            if ma:
                layout.prop(ma, "name", text="", emboss=False, icon_value=layout.icon(ma))


class SceneMaterialsPanel(bpy.types.Panel):

    bl_label = bl_info["name"]
    bl_idname = "SCENE_PT_materials"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = "Scene"

    def draw(self, context):

        scn = context.scene
        layout = self.layout
        col = layout.column()
        if scn.update_materials:
            col.prop(scn, "update_materials", toggle=True, icon='FILE_REFRESH')
        col.template_list(
            "MATERIAL_UL_extreme_matslot",
            "",
            scn,
            "material_slots",
            scn,
            "active_material_index")


classes = (SceneMaterialSlot,
           MATERIAL_UL_extreme_matslot,
           SceneMaterialsPanel)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.materials = property(get_scene_materials)
    bpy.types.Scene.update_materials = BoolProperty(
        get=get_update_materials,
        set=set_update_materials,
        name="Update Scene Materials")
    bpy.types.Scene.active_material_index = IntProperty()
    bpy.types.Scene.material_slots = CollectionProperty(
        type=SceneMaterialSlot)


def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)

    del bpy.types.Scene.active_material_index
    del bpy.types.Scene.material_slots


if __name__ == "__main__":
    register()
        

Edit.

Updated code to make it installable as an addon. (Add a filters to show only materials of selected / visible / etc objects in scene)

Related: Your layout code was nuffed, see @p2or's fabulous answer to

Create an interface which is similar to the material list box

Notes:

Not sure how would correlate scene material index vs active object material index. Rather than using the list UI a more basic UI, with labels

enter image description here

    for m in scn.materials:
        col.label(text=m.name, icon_value=layout.icon(m))

or with operators could be considered.

$\endgroup$
3
  • $\begingroup$ This solution is fantastic ,I still find it hard to understand how it works, I suppose I can list anything like objects, modifiers, etc. ? $\endgroup$
    – Noob Cat
    Commented May 24, 2019 at 3:42
  • 1
    $\begingroup$ objects, materials are ideal since they need unique names. Modifers not so. btw I like where this is going in conjunction with recent rgb answer... If you need help with something in particular comment and I will elaborate. Always try and answer with a running code snippet, which I think speaks for itself rather than filling with comments. Comment over-bloat like # import the bpy module is.... $\endgroup$
    – batFINGER
    Commented May 24, 2019 at 10:52
  • $\begingroup$ Ty bat, is possible store bpy.types (In that script you put in register) but i think, i have a pointerproperty with Class propertygroup, but you use .self.materials etc. I didn't want to create too much confusion in the register, since I've already registered a Pointerproperty $\endgroup$
    – Noob Cat
    Commented May 24, 2019 at 14:33

You must log in to answer this question.

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