Paste to “Scripting” workspace and Alt+P
this ~120 LoC of Python code implements Add>templates>Python>
UI Panel; Menu; Icon(thumbnail grid)
Why can’t blender’s Data-UI codes turn to this declarative way?
@blPanel("My Menu", "OBJECT_MT_helo")
def myMenu(o,c):
ui(o,
ui.op("wm.open_mainfile"),
ui.op("wm.save_as_mainfile"),
ui.op("object.shade_smooth"),
ui.l(text="Hello world!", icon='WORLD_DATA'),
ui.menus("object.select_by_type",property="type", text="all Type"),
ui.op("wm.call_menu", text="Unwrap", e=[vaset(name = "VIEW3D_MT_uv_map")] ))
alternative to:
class CustomMenu(bpy.types.Menu):
bl_label = "Custom Menu"
bl_idname = "OBJECT_MT_custom_menu"
def draw(self, context):
layout = self.layout
layout.operator("wm.open_mainfile")
layout.operator("wm.save_as_mainfile").copy = True
layout.operator("object.shade_smooth")
layout.label(text="Hello world!", icon='WORLD_DATA')
# use an operator enum property to populate a sub-menu
layout.operator_menu_enum("object.select_by_type",
property="type",
text="Select All by Type",
)
# call another menu
layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map"
full:
import bpy
import bpy.utils as U
def each(a,f):
for x in a:f(x)
apart1=lambda f:lambda *a,**kw:lambda x1:f(*a,x1,**kw)
vaset=lambda **kw:lambda o: each(kw.items(),lambda x: setattr(o,*x))
def vagetr(f,fw):
class D:
def __getattribute__(o,k):return f(k)
def __setattr__(o,k,v):fw((k,v))
return D()
def also(x,f):f(x);return x
class _ui:
kt=dict(l="label",r="row",op="operator",col="column", # ui.r(..)(x)=x.row(..)
menus="operator_menu_enum",menuIcon="operator_enum")
pOf=lambda o:vagetr(lambda k:ui.prop(o,k), _propDefOn(o) ) # _ui.pOf(x).k=ui.prop(x,"k")
def __call__(o, ui,*a):
for x in a: o.l(text=x)(ui) if isinstance(x,str) else x(ui)
return ui
def __getattribute__(o,k):
def aUi(*a,e=[],**kw):return lambda e0: o(getattr(e0,_ui.kt.get(k,k)) (*a,**kw), *e) #^14
return aUi
ui=_ui(); plug=[]
def register():each(plug,U.register_class)
def unregister():each(plug,U.unregister_class)
@apart1
def blPanel(name,k,f, kUI="PROPERTIES WINDOW",fin=None):
class _:
bl_label = name;bl_idname = k
def draw(o,c):f(o.layout,c)
def unregister(o):fin() if fin else 0
if -1!= (i:=k.find("_MT_")):
class A(_,bpy.types.Menu): show=lambda:(bpy.ops.wm.call_menu_pie if k.startswith("PIE",i+4) else bpy.ops.wm.call_menu)(name=k)
else:
i=k.index("_PT_"); k0,k1=kUI.split()
class A(_,bpy.types.Panel):
bl_region_type = k1
bl_space_type = k0; bl_context = k[:i].lower()
plug.append(A);return A
Tys={set:"Enum",list:"Collection",str:"String",object:"Pointer", bool:0,int:0,float:0}
for k,v in Tys.items():Tys[k]=getattr(bpy.props,f"{v if v else k.__name__.capitalize()}Property")
@apart1
def _propDefOn(o,kv):
T=getattr(bpy.types,o.bl_rna.identifier)
k,arg=kv
def ty(t,tt,s="",v0=None):
return Tys[t](subtype=tt, description=s, default=v0) if t!=set else Tys[t](items=tt)
setattr(T,k, ty(*arg) )
# samples
@blPanel("Hello World Panel","OBJECT_PT_hello")
def lay(o,c, br=ui.r()):
it=c.object
ui(o, br,
ui.l(text="Hello world!", icon='WORLD_DATA'), br,
"Active object is: " + it.name, br,
ui.prop(it,"name"), br,
ui.op("mesh.primitive_cube_add"))
@blPanel("Hello wo", "OBJECT_PT_helo")
def lay(o,c):
S=_ui.pOf(c.scene)
ui(o, ui.r(e=["fdfd",ui.op("wm.open_mainfile"), ui.op("render.render")]), S.frame_current )
@blPanel("My Menu", "OBJECT_MT_helo")
def myMenu(o,c):
ui(o,
ui.op("wm.open_mainfile"),
ui.op("wm.save_as_mainfile"),
ui.op("object.shade_smooth"),
ui.l(text="Hello world!", icon='WORLD_DATA'),
ui.menus("object.select_by_type",property="type", text="all Type"),
ui.op("wm.call_menu", text="Unwrap", e=[vaset(name = "VIEW3D_MT_uv_map")] ))
@blPanel("Select Mode","VIEW3D_MT_PIE_template")
def myPie(o,c):
ui.menu_pie(e=[ui.menuIcon("mesh.select_mode", "type")]) (o)
@blPanel("Layout Demo","SCENE_PT_demo")
def lay(o,c):
S=_ui.pOf(c.scene); uis=[S.frame_start,S.frame_end];ren=ui.op("render.render")
ui(o,
" Simple Row:",ui.r(e=uis),
" Aligned Row:",ui.r(align=True,e=uis),
ui.split(e=[
ui.col(e=["Column One:", *uis]),
ui.col(align=True,e=["Column Two:", *uis])
]),
"Big Button:",
ui.r(e=[ren, vaset(scale_y = 3.0)]),
"Different button sizes:",
ui.r(align=True,e=[ren, ui.r(e=[vaset(scale_x = 2.0),ren]) ,ren]) )
import bpy.utils.previews as UP
# dynamic icon(>= custom icon)
wm=bpy.context.window_manager
previewers={"main":also(UP.new(), vaset(my_previews_dir = "",my_previews = ()))}
def filterImg(fp,po):
import os
if fp == po.my_previews_dir: return po.my_previews
print("Scanning directory: %s" %fp); res=[]
if fp and os.path.exists(fp):
# Scan the directory for png files
fps = [x for x in os.listdir(fp) if x.lower().endswith(".png")]
for i, name in enumerate(fps):
icon = po.get(name)or po.load(name, os.path.join(fp, name), 'IMAGE') #don't use both get()&has()
res.append((name, name, "", icon.icon_id, i))
po.my_previews_dir = fp; po.my_previews = res #^fn:L1
return res
@blPanel("Previews Example Panel","OBJECT_PT_previews", fin=lambda: each(previewers.values(), UP.remove))
def lay(o,c):
k="my_previews"
ui(o, ui.prop(wm,k+"_dir"), ui.template_icon_view(wm,k), ui.prop(wm,k))
ty=_ui.pOf(wm)
ty.my_previews_dir=(str,'DIR_PATH', "Folder Path","")
ty.my_previews=(set,lambda o,c:filterImg(wm.my_previews_dir,previewers["main"]) if o!=None else [])
register()
myMenu.show();myPie.show()
minimal:
import bpy
apart1=lambda f:lambda *a,**kw:lambda x1:f(*a,x1,**kw)
vaset=lambda **kw:lambda o: each(kw.items(),lambda x: setattr(o,*x))
class _ui:
kt=dict(l="label",r="row",op="operator",col="column", # ui.r(..)(x)=x.row(..)
menus="operator_menu_enum",menuIcon="operator_enum")
def __call__(o, ui,*a):
for x in a: o.l(text=x)(ui) if isinstance(x,str) else x(ui)
return ui
def __getattribute__(o,k):
def aUi(*a,e=[],**kw):return lambda e0: o(getattr(e0,_ui.kt.get(k,k)) (*a,**kw), *e) #^14
return aUi
ui=_ui(); plug=[]
@apart1
def blPanel(name,k,f, kUI="PROPERTIES WINDOW",fin=None):
class _:
bl_label = name;bl_idname = k
def draw(o,c):f(o.layout,c)
def unregister(o):fin() if fin else 0
if -1!= (i:=k.find("_MT_")):
class A(_,bpy.types.Menu): show=lambda:(bpy.ops.wm.call_menu_pie if k.startswith("PIE",i+4) else bpy.ops.wm.call_menu)(name=k)
else:
i=k.index("_PT_"); k0,k1=kUI.split()
class A(_,bpy.types.Panel):
bl_region_type = k1
bl_space_type = k0; bl_context = k[:i].lower()
plug.append(A);return A
@blPanel("My Menu", "OBJECT_MT_helo")
def myMenu(o,c):
ui(o,
ui.op("wm.open_mainfile"),
ui.op("wm.save_as_mainfile"),
ui.op("object.shade_smooth"),
ui.l(text="Hello world!", icon='WORLD_DATA'),
ui.menus("object.select_by_type",property="type", text="all Type"),
ui.op("wm.call_menu", text="Unwrap", e=[vaset(name = "VIEW3D_MT_uv_map")] ))
myMenu.show()