graphic:python:blender

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
Next revisionBoth sides next revision
graphic:python:blender [2024/01/31 07:41] – [Blender variable naming standards] yinggraphic:python:blender [2024/04/09 10:46] – [My related dev notes] ying
Line 437: Line 437:
     value: IntProperty()     value: IntProperty()
  
 +class ToolProperties(bpy.types.PropertyGroup):
 +    commandLineMode: bpy.props.BoolProperty(
 +        name = "Command line mode",
 +        description = "The option specifies if the addon is used in the command line mode",
 +        default = False
 +    ) 
 +    
 +class ToolProps(bpy.types.PropertyGroup):
 +    email : StringProperty(
 +        name="email",
 +        description="User email",
 +        default=""
 +    )
 +class TOOL_PROPS(bpy.types.PropertyGroup):
 +    lon: FloatProperty()
 +    lat: FloatProperty()
 +class BUILDMAKER_PG_color(PropertyGroup):
 +    color: FloatVectorProperty(subtype='COLOR', min=0, max=1, update=updColor, size=4)
 </code> </code>
 ===== common blender api commands ===== ===== common blender api commands =====
Line 616: Line 634:
 ... ...
 layout.template_list(type_name, list_id, ptr, propname, active, rows, maxrows, ..) # a list widget layout.template_list(type_name, list_id, ptr, propname, active, rows, maxrows, ..) # a list widget
 +</code>
 +  * long label solution and auto wrap <code python>
 +import bpy
 +import textwrap
 +
 +long_text = """
 +        a long text  long text  long text
 +        b test
 +        c long text  long text  long text  long text  long text  long
 +        d text  long text  long text  long text """
 +
 +def get_max_label_width():
 +    # Get the 3D View area
 +    for area in bpy.context.screen.areas:
 +        if area.type == 'VIEW_3D':
 +            break
 +    # Calculate the width of the panel
 +    panel_width = 280 # default value
 +    for region in area.regions:
 +        if region.type == 'UI':
 +            panel_width = region.width
 +            return panel_width
 +            break
 +    # Calculate the maximum width of the label
 +    uifontscale = 9 * context.preferences.view.ui_scale
 +    max_label_width = int(panel_width // uifontscale)
 +    return max_label_width
 +
 +def create_long_label(layout, long_text, icon='',indent=''):
 +    max_label_width = get_max_label_width()
 +    first_icon_line_done = False
 +    rest_line_indent = indent
 +    if icon == '':
 +        first_icon_line_done = True
 +        rest_line_indent = ''
 +    # Split the text into lines and format each line
 +    for line in long_text.splitlines():
 +        # Remove leading and trailing whitespace
 +        line = line.strip()
 +        # Split the line into chunks that fit within the maximum label width
 +        for chunk in textwrap.wrap(line, width=max_label_width):
 +            if not first_icon_line_done:
 +                layout.label(text=chunk, icon=icon)
 +                first_icon_line_done = True
 +            else:
 +                layout.label(text=rest_line_indent+chunk)
 +
 +class MyPanel(bpy.types.Panel):
 +    bl_idname = "OBJECT_PT_my_panel"
 +    bl_label = "My Panel"
 +    bl_space_type = "VIEW_3D"
 +    bl_region_type = "UI"
 +    
 +    def draw(self, context):
 +        layout = self.layout
 +        create_long_label(layout, long_text, "KEYTYPE_JITTER_VEC",   ')
 +        
 +
 +# Register the panel
 +bpy.utils.register_class(MyPanel)
 </code> </code>
 ===== Blender preference ===== ===== Blender preference =====
Line 801: Line 879:
     * (Darkfall studio - tut,shorts,anime) https://www.youtube.com/@DarkfallBlender     * (Darkfall studio - tut,shorts,anime) https://www.youtube.com/@DarkfallBlender
     * (Luca Chirichella - blender tut) https://www.youtube.com/@RedKProduction     * (Luca Chirichella - blender tut) https://www.youtube.com/@RedKProduction
 +
 +====== My Blender addon template ======
 +
 +<code python>
 +import os
 +
 +import bpy
 +from bpy.types import AddonPreferences, PropertyGroup, UIList, Operator
 +from bpy.props import (
 +    StringProperty,
 +    IntProperty,
 +    CollectionProperty,
 +    EnumProperty,
 +    PointerProperty,
 +)
 +
 +bl_info = {
 +    "name": "UniversalTool",
 +    "description": "A demo template tool shows assets import and publish",
 +    "author": "Ying",
 +    "version": (1, 0, 0),
 +    "blender": (3, 2, 0),
 +    "location": "3D View > Tools",
 +    "category": "Object",
 +}
 +
 +
 +# =======================================
 +#  preference
 +# =======================================
 +class UniversalAddonPreferences(AddonPreferences):
 +    bl_idname = "UniversalTool"
 +
 +    asset_author_name: StringProperty(
 +        name="Asset Author Name",
 +        default="",
 +        description="Enter the name of the asset author",
 +    )
 +
 +    def draw(self, context):
 +        layout = self.layout
 +        layout.prop(self, "asset_author_name")
 +
 +
 +# =======================================
 +#  properties
 +# =======================================
 +class VariationItem(PropertyGroup):
 +    name: StringProperty(name="Name")
 +    variation_type: StringProperty(name="Type")
 +
 +
 +class VariationProps(PropertyGroup):
 +    variation_type_enum_items = [
 +        ("choice", "Visible Choice random", ""),
 +        ("option", "Visible Option random", ""),
 +        ("stack", "Visibile Stack end random", ""),
 +        ("linear", "Value Linear random", ""),
 +        ("step", "Value Step random", ""),
 +        ("material", "Value Material random", ""),
 +    ]
 +    variation_type: EnumProperty(
 +        items=variation_type_enum_items,
 +        name="Variation Type",
 +        description="Variation Type",
 +        default="choice",
 +    )
 +
 +
 +class PublishSetting(PropertyGroup):
 +    publish_type_enum_items = [
 +        ("new_asset", "New Asset", ""),
 +        ("update_asset", "Asset Update", ""),
 +    ]
 +    publish_name: StringProperty(
 +        name="Name", description="Asset name", default="", maxlen=1024
 +    )
 +    publish_version: IntProperty(
 +        name="Version", description="Version number.", default=1, min=1, max=9999
 +    )
 +    publish_dir: StringProperty(
 +        name="Publish Dir",
 +        description=("Asset Publish Directory."),
 +        subtype="DIR_PATH",
 +    )
 +    publish_type: EnumProperty(
 +        items=publish_type_enum_items,
 +        name="Publish Type",
 +        description="Publish Type",
 +        default="new_asset",
 +    )
 +
 +
 +# =======================================
 +#  operators
 +# =======================================
 +class AddCube(bpy.types.Operator):
 +    bl_idname = "custom.universal_add_cube"
 +    bl_label = "Simple Add Cube"
 +    bl_options = {"REGISTER", "UNDO" # Enable undo for this operator.
 +
 +    def execute(self, context):
 +        bpy.ops.mesh.primitive_cube_add()
 +        return {"FINISHED"}
 +
 +
 +# example of AddObject operator class without UNIVERSAL_OT_AddObject naming pattern
 +class AddObject(Operator):
 +    bl_idname = "custom.universal_add_object"
 +    bl_label = "Add Object"
 +
 +    def execute(self, context):
 +        obj = bpy.context.active_object
 +        wm = context.window_manager
 +        if obj:
 +            item = wm.variation_list.add()
 +            item.name = ",".join([x.name for x in context.selected_objects])
 +            item.variation_type = wm.variation_props.variation_type
 +        return {"FINISHED"}
 +
 +
 +class UNIVERSAL_OT_RemoveObject(Operator):
 +    bl_idname = "custom.universal_remove_object"
 +    bl_label = "Remove Object"
 +    bl_options = {"REGISTER"}
 +
 +    @classmethod
 +    def poll(cls, context):
 +        wm = context.window_manager
 +        return len(wm.variation_list) > 0
 +
 +    def execute(self, context):
 +        wm = context.window_manager
 +        idx = wm.variation_list_index
 +        wm.variation_list.remove(idx)
 +        return {"FINISHED"}
 +
 +
 +class PublishAsset(Operator):
 +    bl_idname = "custom.universal_publish_asset"
 +    bl_label = "Publish Asset"
 +
 +    @classmethod
 +    def poll(self, context):
 +        wm = context.window_manager
 +        publish_prop = wm.publish_setting_props
 +        return (
 +            os.path.isdir(publish_prop.publish_dir)
 +            and publish_prop.publish_name.strip() != ""
 +        )
 +
 +    def execute(self, context):
 +        wm = context.window_manager
 +        publish_prop = wm.publish_setting_props
 +        print(publish_prop.publish_dir)
 +        if publish_prop.publish_type != "new_asset":
 +            self.report({"ERROR"}, "Publish type must be new. (demo of error)")
 +            return {"FINISHED"}
 +        self.report({"INFO"}, "Publish successfully")
 +        return {"FINISHED"}
 +
 +
 +# =======================================
 +#  UI List
 +# =======================================
 +class UNIVERSAL_UL_VariationList(UIList):
 +    def draw_item(
 +        self, context, layout, data, item, icon, active_data, active_propname, index
 +    ):
 +        custom_icon = "BOOKMARKS"
 +        if self.layout_type in {"DEFAULT", "COMPACT"}:
 +            split = layout.split(factor=0.6)
 +            split.label(text=item.name, icon=custom_icon)
 +            split.label(text=item.variation_type)
 +        elif self.layout_type in {"GRID"}:
 +            layout.alignment = "CENTER"
 +            layout.label(text=item.name, icon=custom_icon)
 +            layout.label(text=item.variation_type)
 +
 +
 +# =======================================
 +#  panels
 +# =======================================
 +# UNIVERSAL_PT_VariationPanel: blender naming format
 +# VariationPanel: Python naming format results warning message
 +#    Warning: 'VariationPanel' does not contain '_PT_' with prefix and suffix
 +class UNIVERSAL_PT_VariationPanel(bpy.types.Panel):
 +    bl_space_type = "VIEW_3D"
 +    bl_region_type = "UI"
 +    bl_context = ""
 +    bl_category = "Universal"  # SIDE the tab name
 +    bl_label = "Universal Tool Panel"  # tab tab > panel name
 +
 +    def draw(self, context):
 +        wm = context.window_manager
 +        layout = self.layout
 +        publish_prop = wm.publish_setting_props
 +
 +        layout.operator("custom.universal_add_cube")
 +        layout.label(text="Variation Object List:", icon="EXPORT")
 +
 +        row = layout.row()
 +        row.template_list(
 +            listtype_name="UNIVERSAL_UL_VariationList",
 +            list_id="",
 +            dataptr=wm,
 +            propname="variation_list",
 +            active_dataptr=wm,
 +            active_propname="variation_list_index",
 +        )
 +
 +        layout.prop(wm.variation_props, "variation_type")
 +        row = layout.row()
 +        row.operator("custom.universal_add_object", icon="ADD", text="Add Object")
 +        row.operator(
 +            "custom.universal_remove_object", icon="REMOVE", text="Remove Object"
 +        )
 +
 +        layout.prop(publish_prop, "publish_name", text="Super Name")
 +        layout.prop(publish_prop, "publish_version")
 +        layout.prop(publish_prop, "publish_dir")
 +        layout.prop(publish_prop, "publish_type", expand=True)
 +        asset_author_name = bpy.context.preferences.addons[
 +            "UniversalTool"
 +        ].preferences.asset_author_name.replace(" ", "-")
 +        if (
 +            os.path.isdir(publish_prop.publish_dir)
 +            and publish_prop.publish_name.strip() != ""
 +        ):
 +            result_file = os.path.join(
 +                publish_prop.publish_dir,
 +                publish_prop.publish_name.strip()
 +                + f"_v{publish_prop.publish_version}_{asset_author_name}.json",
 +            )
 +            layout.label(text=f"output: {result_file}")
 +        else:
 +            layout.label(text="output: not valid setting")
 +        layout.operator("custom.universal_publish_asset")
 +
 +
 +# =======================================
 +#  class list to register
 +# =======================================
 +classes = (
 +    UniversalAddonPreferences,
 +    # properties
 +    VariationItem,
 +    VariationProps,
 +    PublishSetting,
 +    # operators
 +    AddCube,
 +    AddObject,
 +    UNIVERSAL_OT_RemoveObject,
 +    PublishAsset,
 +    # ui
 +    UNIVERSAL_UL_VariationList,
 +    UNIVERSAL_PT_VariationPanel,
 +)
 +
 +
 +def register():
 +    for cls in classes:
 +        bpy.utils.register_class(cls)
 +    bpy.types.WindowManager.variation_list = CollectionProperty(type=VariationItem)
 +    bpy.types.WindowManager.variation_list_index = IntProperty(default=0)
 +    bpy.types.WindowManager.variation_props = PointerProperty(type=VariationProps)
 +    bpy.types.WindowManager.publish_setting_props = PointerProperty(type=PublishSetting)
 +
 +
 +def unregister():
 +    del bpy.types.WindowManager.variation_list
 +    del bpy.types.WindowManager.variation_list_index
 +    del bpy.types.WindowManager.variation_type_props
 +    del bpy.types.WindowManager.publish_setting_props
 +    for cls in classes:
 +        bpy.utils.unregister_class(cls)
 +
 +
 +if __name__ == "__main__":
 +    register()
 +
 +</code>
 +
  • graphic/python/blender.txt
  • Last modified: 2024/05/15 04:30
  • by ying