Scene.material_slots
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
for m in scn.materials:
col.label(text=m.name, icon_value=layout.icon(m))
or with operators could be considered.