forked from bartvdbraak/blender
UIList: update examples and templates.
This commit is contained in:
parent
7e73620a98
commit
b7e2cd5948
@ -22,7 +22,10 @@ class MATERIAL_UL_matslots_example(bpy.types.UIList):
|
||||
# active item of the collection).
|
||||
# active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
|
||||
# index is index of the current item in the collection.
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
# flt_flag is the result of the filtering process for this item.
|
||||
# Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
|
||||
# need them.
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
ob = data
|
||||
slot = item
|
||||
ma = slot.material
|
160
doc/python_api/examples/bpy.types.UIList.2.py
Normal file
160
doc/python_api/examples/bpy.types.UIList.2.py
Normal file
@ -0,0 +1,160 @@
|
||||
"""
|
||||
Advanced UIList Example - Filtering and Reordering
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
This script is an extended version of the UIList subclass used to show vertex groups. It is not used 'as is',
|
||||
because iterating over all vertices in a 'draw' function is a very bad idea for UI performances! However, it's a good
|
||||
example of how to create/use filtering/reordering callbacks.
|
||||
"""
|
||||
import bpy
|
||||
|
||||
|
||||
class MESH_UL_vgroups_slow(UIList):
|
||||
# Constants (flags)
|
||||
# Be careful not to shadow FILTER_ITEM!
|
||||
VGROUP_EMPTY = 1 << 0
|
||||
|
||||
# Custom properties, saved with .blend file.
|
||||
use_filter_empty = bpy.props.BoolProperty(name="Filter Empty", default=False, options=set(),
|
||||
description="Whether to filter empty vertex groups")
|
||||
use_filter_empty_reverse = bpy.props.BoolProperty(name="Reverse Empty", default=False, options=set(),
|
||||
description="Reverse empty filtering")
|
||||
use_filter_name_reverse = bpy.props.BoolProperty(name="Reverse Name", default=False, options=set(),
|
||||
description="Reverse name filtering")
|
||||
|
||||
# This allows us to have mutually exclusive options, which are also all disable-able!
|
||||
def _gen_order_update(name1, name2):
|
||||
def _u(self, ctxt):
|
||||
if (getattr(self, name1)):
|
||||
setattr(self, name2, False)
|
||||
return _u
|
||||
use_order_name = bpy.props.BoolProperty(name="Name", default=False, options=set(),
|
||||
description="Sort groups by their name (case-insensitive)",
|
||||
update=_gen_order_update("use_order_name", "use_order_importance"))
|
||||
use_order_importance = bpy.props.BoolProperty(name="Importance", default=False, options=set(),
|
||||
description="Sort groups by their average weight in the mesh",
|
||||
update=_gen_order_update("use_order_importance", "use_order_name"))
|
||||
|
||||
# Usual draw item function.
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
|
||||
# Just in case, we do not use it here!
|
||||
self.use_filter_invert = False
|
||||
|
||||
# assert(isinstance(item, bpy.types.VertexGroup)
|
||||
vgroup = item
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
# Here we use one feature of new filtering feature: it can pass data to draw_item, through flt_flag
|
||||
# parameter, which contains exactly what filter_items set in its filter list for this item!
|
||||
# In this case, we show empty groups grayed out.
|
||||
if flt_flag & self.VGROUP_EMPTY:
|
||||
col = layout.column()
|
||||
col.enabled = False
|
||||
col.alignment = 'LEFT'
|
||||
col.label(text=vgroup.name, translate=False, icon_value=icon)
|
||||
else:
|
||||
layout.label(text=vgroup.name, translate=False, icon_value=icon)
|
||||
icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED'
|
||||
layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False)
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
if flt_flag & self.VGROUP_EMPTY:
|
||||
layout.enabled = False
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
# Nothing much to say here, it's usual UI code...
|
||||
row = layout.row()
|
||||
|
||||
subrow = row.row(align=True)
|
||||
subrow.prop(self, "filter_name", text="")
|
||||
icon = 'ZOOM_OUT' if self.use_filter_name_reverse else 'ZOOM_IN'
|
||||
subrow.prop(self, "use_filter_name_reverse", text="", icon=icon)
|
||||
|
||||
subrow = row.row(align=True)
|
||||
subrow.prop(self, "use_filter_empty", toggle=True)
|
||||
icon = 'ZOOM_OUT' if self.use_filter_empty_reverse else 'ZOOM_IN'
|
||||
subrow.prop(self, "use_filter_empty_reverse", text="", icon=icon)
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.label("Order by:")
|
||||
row.prop(self, "use_order_name", toggle=True)
|
||||
row.prop(self, "use_order_importance", toggle=True)
|
||||
icon = 'TRIA_UP' if self.use_filter_orderby_invert else 'TRIA_DOWN'
|
||||
row.prop(self, "use_filter_orderby_invert", text="", icon=icon)
|
||||
|
||||
def filter_items_empty_vgroups(self, context, vgroups):
|
||||
# This helper function checks vgroups to find out whether they are empty, and what's their average weights.
|
||||
# TODO: This should be RNA helper actually (a vgroup prop like "raw_data: ((vidx, vweight), etc.)").
|
||||
# Too slow for python!
|
||||
obj_data = context.active_object.data
|
||||
ret = {vg.index: [True, 0.0] for vg in vgroups}
|
||||
if hasattr(obj_data, "vertices"): # Mesh data
|
||||
if obj_data.is_editmode:
|
||||
import bmesh
|
||||
bm = bmesh.from_edit_mesh(obj_data)
|
||||
# only ever one deform weight layer
|
||||
dvert_lay = bm.verts.layers.deform.active
|
||||
fact = 1 / len(bm.verts)
|
||||
if dvert_lay:
|
||||
for v in bm.verts:
|
||||
for vg_idx, vg_weight in v[dvert_lay].items():
|
||||
ret[vg_idx][0] = False
|
||||
ret[vg_idx][1] += vg_weight * fact
|
||||
else:
|
||||
fact = 1 / len(obj_data.vertices)
|
||||
for v in obj_data.vertices:
|
||||
for vg in v.groups:
|
||||
ret[vg.group][0] = False
|
||||
ret[vg.group][1] += vg.weight * fact
|
||||
elif hasattr(obj_data, "points"): # Lattice data
|
||||
# XXX no access to lattice editdata?
|
||||
fact = 1 / len(obj_data.points)
|
||||
for v in obj_data.points:
|
||||
for vg in v.groups:
|
||||
ret[vg.group][0] = False
|
||||
ret[vg.group][1] += vg.weight * fact
|
||||
return ret
|
||||
|
||||
def filter_items(self, context, data, propname):
|
||||
# This function gets the collection property (as the usual tuple (data, propname)), and must return two lists:
|
||||
# * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the
|
||||
# matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the
|
||||
# first one to mark VGROUP_EMPTY.
|
||||
# * The second one is for reordering, it must return a list containing the new indices of the items (which
|
||||
# gives us a mapping org_idx -> new_idx).
|
||||
# Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).
|
||||
# If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than
|
||||
# returning full lists doing nothing!).
|
||||
vgroups = getattr(data, propname)
|
||||
helper_funcs = bpy.types.UI_UL_list
|
||||
|
||||
# Default return values.
|
||||
flt_flags = []
|
||||
flt_neworder = []
|
||||
|
||||
# Pre-compute of vgroups data, CPU-intensive. :/
|
||||
vgroups_empty = self.filter_items_empty_vgroups(context, vgroups)
|
||||
|
||||
# Filtering by name
|
||||
if self.filter_name:
|
||||
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, vgroups, "name",
|
||||
reverse=self.use_filter_name_reverse)
|
||||
if not flt_flags:
|
||||
flt_flags = [self.bitflag_filter_item] * len(vgroups)
|
||||
|
||||
# Filter by emptiness.
|
||||
for idx, vg in enumerate(vgroups):
|
||||
if vgroups_empty[vg.index][0]:
|
||||
flt_flags[idx] |= self.VGROUP_EMPTY
|
||||
if self.use_filter_empty and self.use_filter_empty_reverse:
|
||||
flt_flags[idx] &= ~self.bitflag_filter_item
|
||||
elif self.use_filter_empty and not self.use_filter_empty_reverse:
|
||||
flt_flags[idx] &= ~self.bitflag_filter_item
|
||||
|
||||
# Reorder by name or average weight.
|
||||
if self.use_order_name:
|
||||
flt_neworder = helper_funcs.sort_items_by_name(vgroups, "name")
|
||||
elif self.use_order_importance:
|
||||
_sort = [(idx, vgroups_empty[vg.index][1]) for idx, vg in enumerate(vgroups)]
|
||||
flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], True)
|
||||
|
||||
return flt_flags, flt_neworder
|
@ -1,79 +1,45 @@
|
||||
import bpy
|
||||
|
||||
|
||||
class MATERIAL_UL_matslots_example(bpy.types.UIList):
|
||||
# The draw_item function is called for each item of the collection that is visible in the list.
|
||||
# data is the RNA object containing the collection,
|
||||
# item is the current drawn item of the collection,
|
||||
# icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
|
||||
# have custom icons ID, which are not available as enum items).
|
||||
# active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
|
||||
# active item of the collection).
|
||||
# active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
|
||||
# index is index of the current item in the collection.
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
ob = data
|
||||
slot = item
|
||||
ma = slot.material
|
||||
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
|
||||
class MESH_UL_mylist(UIList):
|
||||
# Constants (flags)
|
||||
# Be careful not to shadow FILTER_ITEM (i.e. UIList().bitflag_filter_item)!
|
||||
# E.g. VGROUP_EMPTY = 1 << 0
|
||||
|
||||
# Custom properties, saved with .blend file. E.g.
|
||||
# use_filter_empty = bpy.props.BoolProperty(name="Filter Empty", default=False, options=set(),
|
||||
# description="Whether to filter empty vertex groups")
|
||||
|
||||
# Called for each drawn item.
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
|
||||
# 'DEFAULT' and 'COMPACT' layout types should usually use the same draw code.
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
# You should always start your row layout by a label (icon + text), this will also make the row easily
|
||||
# selectable in the list!
|
||||
# We use icon_value of label, as our given icon is an integer value, not an enum ID.
|
||||
# Note "data" names should never be translated!
|
||||
layout.label(text=ma.name if ma else "", translate=False, icon_value=icon)
|
||||
# And now we can add other UI stuff...
|
||||
# Here, we add nodes info if this material uses (old!) shading nodes.
|
||||
if ma and not context.scene.render.use_shading_nodes:
|
||||
manode = ma.active_node_material
|
||||
if manode:
|
||||
# The static method UILayout.icon returns the integer value of the icon ID "computed" for the given
|
||||
# RNA object.
|
||||
layout.label(text="Node %s" % manode.name, translate=False, icon_value=layout.icon(manode))
|
||||
elif ma.use_nodes:
|
||||
layout.label(text="Node <none>", translate=False)
|
||||
else:
|
||||
layout.label(text="")
|
||||
pass
|
||||
# 'GRID' layout type should be as compact as possible (typically a single icon!).
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
pass
|
||||
|
||||
# Called once to draw filtering/reordering options.
|
||||
def draw_filter(self, context, layout):
|
||||
# Nothing much to say here, it's usual UI code...
|
||||
pass
|
||||
|
||||
# And now we can use this list everywhere in Blender. Here is a small example panel.
|
||||
class UIListPanelExample(bpy.types.Panel):
|
||||
"""Creates a Panel in the Object properties window"""
|
||||
bl_label = "UIList Panel"
|
||||
bl_idname = "OBJECT_PT_ui_list_example"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
# Called once to filter/reorder items.
|
||||
def filter_items(self, context, data, propname):
|
||||
# This function gets the collection property (as the usual tuple (data, propname)), and must return two lists:
|
||||
# * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the
|
||||
# matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the
|
||||
# first one to mark VGROUP_EMPTY.
|
||||
# * The second one is for reordering, it must return a list containing the new indices of the items (which
|
||||
# gives us a mapping org_idx -> new_idx).
|
||||
# Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).
|
||||
# If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than
|
||||
# returning full lists doing nothing!).
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
# Default return values.
|
||||
flt_flags = []
|
||||
flt_neworder = []
|
||||
|
||||
obj = context.object
|
||||
# Do filtering/reordering here...
|
||||
|
||||
# template_list now takes two new args.
|
||||
# The first one is the identifier of the registered UIList to use (if you want only the default list,
|
||||
# with no custom draw code, use "UI_UL_list").
|
||||
layout.template_list("MATERIAL_UL_matslots_example", "", obj, "material_slots", obj, "active_material_index")
|
||||
|
||||
# The second one can usually be left as an empty string. It's an additional ID used to distinguish lists in case you
|
||||
# use the same list several times in a given area.
|
||||
layout.template_list("MATERIAL_UL_matslots_example", "compact", obj, "material_slots",
|
||||
obj, "active_material_index", type='COMPACT')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(MATERIAL_UL_matslots_example)
|
||||
bpy.utils.register_class(UIListPanelExample)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(MATERIAL_UL_matslots_example)
|
||||
bpy.utils.unregister_class(UIListPanelExample)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
return flt_flags, flt_neworder
|
||||
|
82
release/scripts/templates_py/ui_list_simple.py
Normal file
82
release/scripts/templates_py/ui_list_simple.py
Normal file
@ -0,0 +1,82 @@
|
||||
import bpy
|
||||
|
||||
|
||||
class MATERIAL_UL_matslots_example(bpy.types.UIList):
|
||||
# The draw_item function is called for each item of the collection that is visible in the list.
|
||||
# data is the RNA object containing the collection,
|
||||
# item is the current drawn item of the collection,
|
||||
# icon is the "computed" icon for the item (as an integer, because some objects like materials or textures
|
||||
# have custom icons ID, which are not available as enum items).
|
||||
# active_data is the RNA object containing the active property for the collection (i.e. integer pointing to the
|
||||
# active item of the collection).
|
||||
# active_propname is the name of the active property (use 'getattr(active_data, active_propname)').
|
||||
# index is index of the current item in the collection.
|
||||
# flt_flag is the result of the filtering process for this item.
|
||||
# Note: as index and flt_flag are optional arguments, you do not have to use/declare them here if you don't
|
||||
# need them.
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
ob = data
|
||||
slot = item
|
||||
ma = slot.material
|
||||
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
# You should always start your row layout by a label (icon + text), this will also make the row easily
|
||||
# selectable in the list!
|
||||
# We use icon_value of label, as our given icon is an integer value, not an enum ID.
|
||||
# Note "data" names should never be translated!
|
||||
layout.label(text=ma.name if ma else "", translate=False, icon_value=icon)
|
||||
# And now we can add other UI stuff...
|
||||
# Here, we add nodes info if this material uses (old!) shading nodes.
|
||||
if ma and not context.scene.render.use_shading_nodes:
|
||||
manode = ma.active_node_material
|
||||
if manode:
|
||||
# The static method UILayout.icon returns the integer value of the icon ID "computed" for the given
|
||||
# RNA object.
|
||||
layout.label(text="Node %s" % manode.name, translate=False, icon_value=layout.icon(manode))
|
||||
elif ma.use_nodes:
|
||||
layout.label(text="Node <none>", translate=False)
|
||||
else:
|
||||
layout.label(text="")
|
||||
# 'GRID' layout type should be as compact as possible (typically a single icon!).
|
||||
elif self.layout_type in {'GRID'}:
|
||||
layout.alignment = 'CENTER'
|
||||
layout.label(text="", icon_value=icon)
|
||||
|
||||
|
||||
# And now we can use this list everywhere in Blender. Here is a small example panel.
|
||||
class UIListPanelExample(bpy.types.Panel):
|
||||
"""Creates a Panel in the Object properties window"""
|
||||
bl_label = "UIList Panel"
|
||||
bl_idname = "OBJECT_PT_ui_list_example"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
obj = context.object
|
||||
|
||||
# template_list now takes two new args.
|
||||
# The first one is the identifier of the registered UIList to use (if you want only the default list,
|
||||
# with no custom draw code, use "UI_UL_list").
|
||||
layout.template_list("MATERIAL_UL_matslots_example", "", obj, "material_slots", obj, "active_material_index")
|
||||
|
||||
# The second one can usually be left as an empty string. It's an additional ID used to distinguish lists in case you
|
||||
# use the same list several times in a given area.
|
||||
layout.template_list("MATERIAL_UL_matslots_example", "compact", obj, "material_slots",
|
||||
obj, "active_material_index", type='COMPACT')
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(MATERIAL_UL_matslots_example)
|
||||
bpy.utils.register_class(UIListPanelExample)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(MATERIAL_UL_matslots_example)
|
||||
bpy.utils.unregister_class(UIListPanelExample)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
Loading…
Reference in New Issue
Block a user