You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blender 3.6 Python开发:实现执行Operator后可通过历史操作菜单动态修改PropertyGroup属性以影响操作结果

Blender 3.6 Python开发:实现执行Operator后可通过历史操作菜单动态修改PropertyGroup属性以影响操作结果

我明白你的需求了——你希望不管是在主面板调整属性后执行Operator,还是执行后在历史操作菜单(F9呼出的面板)修改参数,都能让生成的物体同步更新,并且两边的属性值保持一致。你当前的问题在于:Operator执行时只是一次性读取PropertyGroup的值,后续修改历史参数不会关联回PropertyGroup,也不会更新已生成的物体。

下面我给你一套完整的修改方案,一步步解决这个问题:

核心思路

  1. 让Operator支持撤销/重做(通过UNDO选项),这样执行后F9面板才会显示参数
  2. 在Operator启动时,先把PropertyGroup的当前值同步到Operator自身属性
  3. 执行逻辑完全基于Operator自身属性,确保修改历史参数时能生效
  4. 执行完成后,把Operator的参数同步回PropertyGroup,让主面板显示和历史参数一致
  5. 跟踪生成的物体,修改参数时更新(或替换)旧物体,避免场景堆积

修改后的完整代码

import bpy
from bpy.props import (IntProperty, FloatProperty,
                       PointerProperty, PropertyGroup)
from bpy.types import (Operator, Panel, PropertyGroup)

# 自定义属性组
class FBGenProperties(PropertyGroup):
    root_subdiv : IntProperty(
        name = "Chains Amount",
        default = 3, min = 1, max = 100,
        soft_min = 3, soft_max = 20,
        subtype = "FACTOR"
    )
    bone_length : FloatProperty(
        name = "Segments Length",
        default = .1, min = 0.001, max = 100,
        subtype = "DISTANCE"
    )

# 生成Operator
class BonesGen(Operator):
    bl_idname = "object.bones_gen"
    bl_label = "Generate Bones"
    bl_options = {'REGISTER', 'UNDO'}  # 关键:开启注册和撤销重做
    
    # 定义和PropertyGroup对应的Operator属性
    root_subdiv : IntProperty(
        name = "Chains Amount",
        default = 3, min = 1, max = 100,
        soft_min = 3, soft_max = 20,
        subtype = "FACTOR"
    )
    bone_length : FloatProperty(
        name = "Segments Length",
        default = .1, min = 0.001, max = 100,
        subtype = "DISTANCE"
    )
    
    def invoke(self, context, event):
        # 启动时同步PropertyGroup的值到Operator
        obj = context.active_object
        if not obj:
            self.report({'WARNING'}, "请先选中一个对象!")
            return {'CANCELLED'}
        props = obj.FB_gen_props
        self.root_subdiv = props.root_subdiv
        self.bone_length = props.bone_length
        return self.execute(context)
    
    def execute(self, context) -> set:
        obj = context.active_object
        if not obj:
            self.report({'WARNING'}, "请先选中一个对象!")
            return {'CANCELLED'}
        
        # 寻找之前生成的标记物体(避免重复创建)
        generated_obj = None
        for o in context.scene.objects:
            if o.get('is_fb_generated', False):
                generated_obj = o
                break
        
        # 删除旧物体(如果存在)
        if generated_obj:
            bpy.data.objects.remove(generated_obj)
        
        # 根据当前Operator参数创建新物体
        bpy.ops.mesh.primitive_cylinder_add(
            radius=self.root_subdiv,
            depth=self.bone_length,
            location=(0, 0, 0),
            rotation=(0, 0, 0)
        )
        new_obj = context.active_object
        new_obj['is_fb_generated'] = True  # 标记为我们生成的物体
        
        # 同步Operator参数回PropertyGroup,让主面板显示同步更新
        props = obj.FB_gen_props
        props.root_subdiv = self.root_subdiv
        props.bone_length = self.bone_length
        
        return {'FINISHED'}

# 主面板
class MainPanel(Panel):
    bl_idname = "VIEW3D_PT_main_panel"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_label = "骨骼生成器"
    bl_category = "FB工具"  # 自定义UI标签
    
    def draw(self, context):
        layout = self.layout
        obj = context.active_object
        selected = obj.select_get() if obj is not None else False
        name = obj.name if selected else ""
        
        layout.label(text=f"当前激活对象: {name}")
        
        if name != "":
            props = obj.FB_gen_props
            col = layout.column()
            col.prop(props, 'root_subdiv')
            col.prop(props, 'bone_length')
            layout.operator("object.bones_gen", text="生成骨骼(测试圆柱)")

# 注册/注销逻辑
def register():
    bpy.utils.register_class(FBGenProperties)
    bpy.utils.register_class(BonesGen)
    bpy.utils.register_class(MainPanel)
    bpy.types.Object.FB_gen_props = PointerProperty(type=FBGenProperties)

def unregister():
    bpy.utils.unregister_class(FBGenProperties)
    bpy.utils.unregister_class(BonesGen)
    bpy.utils.unregister_class(MainPanel)
    del bpy.types.Object.FB_gen_props

if __name__ == "__main__":
    register()

关键修改点说明

  1. 开启Operator的UNDO选项:这是让历史操作面板显示参数的前提,没有它执行后F9不会弹出参数面板
  2. 添加invoke方法:在用户点击按钮时,先把主面板的PropertyGroup值同步到Operator,确保初始参数和面板一致
  3. 执行逻辑基于Operator自身属性:不再从PropertyGroup读取值,而是直接用self.root_subdiv和self.bone_length,这样修改历史参数时会直接生效
  4. 标记生成的物体:给生成的圆柱加自定义属性is_fb_generated,重做时会自动删除旧物体,避免场景里堆积大量测试物体
  5. 双向同步参数:执行完成后把Operator的参数写回PropertyGroup,让主面板和历史面板的参数保持一致

使用流程测试

  1. 运行脚本后,在3D视图按N键打开侧边栏,找到「FB工具」标签
  2. 选中任意对象,调整「Chains Amount」和「Segments Length」,点击生成按钮,会创建对应参数的圆柱
  3. 按F9呼出历史操作面板,修改参数后回车,旧圆柱会被替换成新参数的圆柱,同时主面板的属性值也会同步更新
  4. 回到主面板修改属性,再次点击生成按钮,历史面板也会显示最新的参数

这样就完全实现了你想要的动态修改属性的效果,不管是在主面板还是历史菜单调整参数,都能得到正确的结果。

备注:内容来源于stack exchange,提问作者Void

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 08:40:29