0

I want to create UI list for each material slot ,

for example if i select "material 01" one it should only display custom_index which are assigned to material 01 , in short each material slot will show different UI list ,

to explain it better here is a video https://youtu.be/0CZBrFv9xVY

this code is little bit working but it does not update on material change , , and i think its connected with entire scene not with each material , please help

i got this code from here https://gist.github.com/p2or/d6dfd47366b2f14816f57d2067dcb6a9

    # ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

bl_info = { "name": "object-uilist-dev", "description": "", "author": "p2or", "version": (0, 1), "blender": (2, 80, 0), "location": "Text Editor", "warning": "", # used for warning icon and text in addons panel "wiki_url": "", "tracker_url": "", "category": "Development" }

import bpy

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

from bpy.types import (Operator, Panel, PropertyGroup, UIList)

-------------------------------------------------------------------

Operators

-------------------------------------------------------------------

class CUSTOM_OT_actions(Operator): """Move items up and down, add and remove""" bl_idname = "custom.list_action" bl_label = "List Actions" bl_description = "Move items up and down, add and remove" bl_options = {'REGISTER'}

action: bpy.props.EnumProperty(
    items=(
        ('UP', "Up", ""),
        ('DOWN', "Down", ""),
        ('REMOVE', "Remove", ""),
        ('ADD', "Add", "")))

def invoke(self, context, event):
    scn = context.scene
    idx = scn.custom_index

    try:
        item = scn.custom[idx]
    except IndexError:
        pass
    else:
        if self.action == 'DOWN' and idx < len(scn.custom) - 1:
            item_next = scn.custom[idx+1].name
            scn.custom.move(idx, idx+1)
            scn.custom_index += 1
            info = 'Item "%s" moved to position %d' % (item.name, scn.custom_index + 1)
            self.report({'INFO'}, info)

        elif self.action == 'UP' and idx >= 1:
            item_prev = scn.custom[idx-1].name
            scn.custom.move(idx, idx-1)
            scn.custom_index -= 1
            info = 'Item "%s" moved to position %d' % (item.name, scn.custom_index + 1)
            self.report({'INFO'}, info)

        elif self.action == 'REMOVE':
            info = 'Item "%s" removed from list' % (scn.custom[idx].name)
            scn.custom_index -= 1
            scn.custom.remove(idx)
            self.report({'INFO'}, info)

    if self.action == 'ADD':
        if context.object:
            item = scn.custom.add()
            item.name = context.object.name
            item.obj_type = context.object.type
            item.obj_id = len(scn.custom)
            scn.custom_index = len(scn.custom)-1
            info = '"%s" added to list' % (item.name)
            self.report({'INFO'}, info)
        else:
            self.report({'INFO'}, "Nothing selected in the Viewport")
    return {"FINISHED"}


class CUSTOM_OT_printItems(Operator): """Print all items and their properties to the console""" bl_idname = "custom.print_items" bl_label = "Print Items to Console" bl_description = "Print all items and their properties to the console" bl_options = {'REGISTER', 'UNDO'}

reverse_order: BoolProperty(
    default=False,
    name="Reverse Order")

@classmethod
def poll(cls, context):
    return bool(context.scene.custom)

def execute(self, context):
    scn = context.scene
    if self.reverse_order:
        for i in range(scn.custom_index, -1, -1):        
            item = scn.custom[i]
            print ("Name:", item.name,"-",item.obj_type,item.obj_id)
    else:
        for item in scn.custom:
            print ("Name:", item.name,"-",item.obj_type,item.obj_id)
    return{'FINISHED'}


class CUSTOM_OT_clearList(Operator): """Clear all items of the list""" bl_idname = "custom.clear_list" bl_label = "Clear List" bl_description = "Clear all items of the list" bl_options = {'INTERNAL'}

@classmethod
def poll(cls, context):
    return bool(context.scene.custom)

def invoke(self, context, event):
    return context.window_manager.invoke_confirm(self, event)

def execute(self, context):
    if bool(context.scene.custom):
        context.scene.custom.clear()
        self.report({'INFO'}, "All items removed")
    else:
        self.report({'INFO'}, "Nothing to remove")
    return{'FINISHED'}


class CUSTOM_OT_removeDuplicates(Operator): """Remove all duplicates""" bl_idname = "custom.remove_duplicates" bl_label = "Remove Duplicates" bl_description = "Remove all duplicates" bl_options = {'INTERNAL'}

def find_duplicates(self, context):
    """find all duplicates by name"""
    name_lookup = {}
    for c, i in enumerate(context.scene.custom):
        name_lookup.setdefault(i.name, []).append(c)
    duplicates = set()
    for name, indices in name_lookup.items():
        for i in indices[1:]:
            duplicates.add(i)
    return sorted(list(duplicates))

@classmethod
def poll(cls, context):
    return bool(context.scene.custom)

def execute(self, context):
    scn = context.scene
    removed_items = []
    # Reverse the list before removing the items
    for i in self.find_duplicates(context)[::-1]:
        scn.custom.remove(i)
        removed_items.append(i)
    if removed_items:
        scn.custom_index = len(scn.custom)-1
        info = ', '.join(map(str, removed_items))
        self.report({'INFO'}, "Removed indices: %s" % (info))
    else:
        self.report({'INFO'}, "No duplicates")
    return{'FINISHED'}

def invoke(self, context, event):
    return context.window_manager.invoke_confirm(self, event)


class CUSTOM_OT_selectItems(Operator): """Select Items in the Viewport""" bl_idname = "custom.select_items" bl_label = "Select Item(s) in Viewport" bl_description = "Select Items in the Viewport" bl_options = {'REGISTER', 'UNDO'}

select_all: BoolProperty(
    default=False,
    name="Select all Items of List",
    options={'SKIP_SAVE'})

@classmethod
def poll(cls, context):
    return bool(context.scene.custom)

def execute(self, context):
    scn = context.scene
    idx = scn.custom_index

    try:
        item = scn.custom[idx]
    except IndexError:
        self.report({'INFO'}, "Nothing selected in the list")
        return{'CANCELLED'}

    obj_error = False
    bpy.ops.object.select_all(action='DESELECT')
    if not self.select_all:
        obj = scn.objects.get(scn.custom[idx].name, None)
        if not obj: 
            obj_error = True
        else:
            obj.select_set(True)
            info = '"%s" selected in Viewport' % (obj.name)
    else:
        selected_items = []
        unique_objs = set([i.name for i in scn.custom])
        for i in unique_objs:
            obj = scn.objects.get(i, None)
            if obj:
                obj.select_set(True)
                selected_items.append(obj.name)

        if not selected_items: 
            obj_error = True
        else:
            missing_items = unique_objs.difference(selected_items)
            if not missing_items:
                info = '"%s" selected in Viewport' \
                    % (', '.join(map(str, selected_items)))
            else:
                info = 'Missing items: "%s"' \
                    % (', '.join(map(str, missing_items)))
    if obj_error: 
        info = "Nothing to select, object removed from scene"
    self.report({'INFO'}, info)    
    return{'FINISHED'}


-------------------------------------------------------------------

Drawing

-------------------------------------------------------------------

class CUSTOM_UL_items(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): split = layout.split(factor=0.3) split.label(text="Index: %d" % (index)) custom_icon = "OUTLINER_OB_%s" % item.obj_type #split.prop(item, "name", text="", emboss=False, translate=False, icon=custom_icon) split.label(text=item.name, icon=custom_icon) # avoids renaming the item by accident

def invoke(self, context, event):
    pass   

class CUSTOM_PT_objectList(Panel): """Adds a custom panel to the TEXT_EDITOR""" bl_idname = 'TEXT_PT_my_panel' bl_space_type = "TEXT_EDITOR" bl_region_type = "UI" bl_label = "Custom Object List Demo"

def draw(self, context):
    layout = self.layout
    scn = bpy.context.scene

    rows = 2
    row = layout.row()
    row.template_list("CUSTOM_UL_items", "", scn, "custom", scn, "custom_index", rows=rows)

    col = row.column(align=True)
    col.operator("custom.list_action", icon='ZOOM_IN', text="").action = 'ADD'
    col.operator("custom.list_action", icon='ZOOM_OUT', text="").action = 'REMOVE'
    col.separator()
    col.operator("custom.list_action", icon='TRIA_UP', text="").action = 'UP'
    col.operator("custom.list_action", icon='TRIA_DOWN', text="").action = 'DOWN'

    row = layout.row()
    col = row.column(align=True)
    row = col.row(align=True)
    row.operator("custom.print_items", icon="LINENUMBERS_ON") #LINENUMBERS_OFF, ANIM
    row = col.row(align=True)
    row.operator("custom.select_items", icon="VIEW3D", text="Select Item")
    row.operator("custom.select_items", icon="GROUP", text="Select all Items").select_all = True
    row = col.row(align=True)
    row.operator("custom.clear_list", icon="X")
    row.operator("custom.remove_duplicates", icon="GHOST_ENABLED")


-------------------------------------------------------------------

Collection

-------------------------------------------------------------------

class CUSTOM_objectCollection(PropertyGroup): #name: StringProperty() -> Instantiated by default obj_type: StringProperty() obj_id: IntProperty()

-------------------------------------------------------------------

Register & Unregister

-------------------------------------------------------------------

classes = ( CUSTOM_OT_actions, CUSTOM_OT_printItems, CUSTOM_OT_clearList, CUSTOM_OT_removeDuplicates, CUSTOM_OT_selectItems, CUSTOM_UL_items, CUSTOM_PT_objectList, CUSTOM_objectCollection, )

def register(): from bpy.utils import register_class for cls in classes: register_class(cls)

# Custom scene properties
bpy.types.Scene.custom = CollectionProperty(type=CUSTOM_objectCollection)
bpy.types.Scene.custom_index = IntProperty()


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

del bpy.types.Scene.custom
del bpy.types.Scene.custom_index


if name == "main": register()

draguu
  • 319
  • 2
  • 7
  • 1
    The indention is pretty messed up in your cut and paste of the code and I think there are bits missing. Can you redo that? – Marty Fouts Jan 03 '22 at 17:43
  • 2
    I would suggest looking at this : https://blender.stackexchange.com/questions/158440/how-to-get-one-uilist-to-control-the-contents-of-another-uilist/ – Reigen Jan 03 '22 at 18:28
  • here is a clean code which i have been using ,

    https://gist.github.com/p2or/d6dfd47366b2f14816f57d2067dcb6a9

    – draguu Jan 04 '22 at 04:35
  • 1
    Have you already seen my answer here: https://blender.stackexchange.com/questions/30444/create-an-interface-which-is-similar-to-the-material-list-box ? I implemented an operator to the material demo which allows to add all materials of the current blend file to the list. Does this help? – p2or Jan 04 '22 at 13:27
  • Yes i have seen your answer , but in your answer you are adding new materials. What i want is individual Ui list template for active material index. Basically i am trying to create easy to use shading workflow addon, so select first material and it will show you some properties in ui list , select another material and it will show you some different ui list props – draguu Jan 04 '22 at 19:21

1 Answers1

0

I tried and found one solution ,

in the custom properties instead of bpy.types.scene. it should be bpy.types.material, so that the collection property will work for materials

# Custom scene properties
bpy.types.Material.custom = CollectionProperty(type=CUSTOM_objectCollection)
bpy.types.Material.custom_index = IntProperty()

than when we use it in panel code it will create collection properties for each material still its yet not updating quickly as we select another material , but its working , and thanks everyone for helping ,

here is a code

import bpy

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

from bpy.types import (Operator, Panel, PropertyGroup, UIList)

-------------------------------------------------------------------

Operators

-------------------------------------------------------------------

class CUSTOM_OT_actions(Operator): """Move items up and down, add and remove""" bl_idname = "custom.list_action" bl_label = "List Actions" bl_description = "Move items up and down, add and remove" bl_options = {'REGISTER'}

action: bpy.props.EnumProperty(
    items=(
        ('UP', "Up", ""),
        ('DOWN', "Down", ""),
        ('REMOVE', "Remove", ""),
        ('ADD', "Add", "")))

def invoke(self, context, event):
    scn = context.object.active_material
    idx = scn.custom_index

    try:
        item = scn.custom[idx]
    except IndexError:
        pass
    else:
        if self.action == 'DOWN' and idx < len(scn.custom) - 1:
            item_next = scn.custom[idx+1].name
            scn.custom.move(idx, idx+1)
            scn.custom_index += 1
            info = 'Item "%s" moved to position %d' % (item.name, scn.custom_index + 1)
            self.report({'INFO'}, info)

        elif self.action == 'UP' and idx >= 1:
            item_prev = scn.custom[idx-1].name
            scn.custom.move(idx, idx-1)
            scn.custom_index -= 1
            info = 'Item "%s" moved to position %d' % (item.name, scn.custom_index + 1)
            self.report({'INFO'}, info)

        elif self.action == 'REMOVE':
            info = 'Item "%s" removed from list' % (scn.custom[idx].name)
            scn.custom_index -= 1
            scn.custom.remove(idx)
            self.report({'INFO'}, info)

    if self.action == 'ADD':
        if context.object:
            item = scn.custom.add()
            item.name = context.object.name
            item.obj_type = context.object.type
            item.obj_id = len(scn.custom)
            scn.custom_index = len(scn.custom)-1
            info = '"%s" added to list' % (item.name)
            self.report({'INFO'}, info)
        else:
            self.report({'INFO'}, "Nothing selected in the Viewport")
    return {"FINISHED"}




-------------------------------------------------------------------

Drawing

-------------------------------------------------------------------

class CUSTOM_UL_items(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): #split = layout.split(factor=0.3) layout.label(text="Index: %d" % (index)) #custom_icon = "OUTLINER_OB_%s" % item.obj_type #split.prop(item, "name", text="", emboss=False, translate=False, icon=custom_icon) layout.label(text='hello', icon=custom_icon) # avoids renaming the item by accident

def invoke(self, context, event):
    pass   

class CUSTOM_PT_objectList(Panel): """Adds a custom panel to the TEXT_EDITOR""" bl_idname = 'TEXT_PT_my_panel' bl_space_type = "TEXT_EDITOR" bl_region_type = "UI" bl_label = "Custom Object List Demo"

def draw(self, context):
    layout = self.layout
    scn = bpy.context.object.active_material

    rows = 2
    row = layout.row()
    row.template_list("CUSTOM_UL_items", "", scn, "custom", scn, "custom_index", rows=rows)

    col = row.column(align=True)
    col.operator("custom.list_action", icon='ZOOM_IN', text="").action = 'ADD'
    col.operator("custom.list_action", icon='ZOOM_OUT', text="").action = 'REMOVE'
    col.separator()
    col.operator("custom.list_action", icon='TRIA_UP', text="").action = 'UP'
    col.operator("custom.list_action", icon='TRIA_DOWN', text="").action = 'DOWN'

    row = layout.row()
    col = row.column(align=True)
    row = col.row(align=True)



-------------------------------------------------------------------

Collection

-------------------------------------------------------------------

class CUSTOM_objectCollection(PropertyGroup): #name: StringProperty() -> Instantiated by default obj_type: StringProperty() obj_id: IntProperty()

-------------------------------------------------------------------

Register & Unregister

-------------------------------------------------------------------

classes = ( CUSTOM_OT_actions, CUSTOM_UL_items, CUSTOM_PT_objectList, CUSTOM_objectCollection, )

def register(): from bpy.utils import register_class for cls in classes: register_class(cls)

# Custom scene properties
bpy.types.Material.custom = CollectionProperty(type=CUSTOM_objectCollection)
bpy.types.Material.custom_index = IntProperty()


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

del bpy.types.Material.custom
del bpy.types.Material.custom_index


if name == "main": register()

draguu
  • 319
  • 2
  • 7