Blender 3.6 Python开发:实现执行Operator后可通过历史操作菜单动态修改PropertyGroup属性以影响操作结果
Blender 3.6 Python开发:实现执行Operator后可通过历史操作菜单动态修改PropertyGroup属性以影响操作结果
我明白你的需求了——你希望不管是在主面板调整属性后执行Operator,还是执行后在历史操作菜单(F9呼出的面板)修改参数,都能让生成的物体同步更新,并且两边的属性值保持一致。你当前的问题在于:Operator执行时只是一次性读取PropertyGroup的值,后续修改历史参数不会关联回PropertyGroup,也不会更新已生成的物体。
下面我给你一套完整的修改方案,一步步解决这个问题:
核心思路
- 让Operator支持撤销/重做(通过
UNDO选项),这样执行后F9面板才会显示参数 - 在Operator启动时,先把PropertyGroup的当前值同步到Operator自身属性
- 执行逻辑完全基于Operator自身属性,确保修改历史参数时能生效
- 执行完成后,把Operator的参数同步回PropertyGroup,让主面板显示和历史参数一致
- 跟踪生成的物体,修改参数时更新(或替换)旧物体,避免场景堆积
修改后的完整代码
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()
关键修改点说明
- 开启Operator的
UNDO选项:这是让历史操作面板显示参数的前提,没有它执行后F9不会弹出参数面板 - 添加
invoke方法:在用户点击按钮时,先把主面板的PropertyGroup值同步到Operator,确保初始参数和面板一致 - 执行逻辑基于Operator自身属性:不再从PropertyGroup读取值,而是直接用
self.root_subdiv和self.bone_length,这样修改历史参数时会直接生效 - 标记生成的物体:给生成的圆柱加自定义属性
is_fb_generated,重做时会自动删除旧物体,避免场景里堆积大量测试物体 - 双向同步参数:执行完成后把Operator的参数写回PropertyGroup,让主面板和历史面板的参数保持一致
使用流程测试
- 运行脚本后,在3D视图按N键打开侧边栏,找到「FB工具」标签
- 选中任意对象,调整「Chains Amount」和「Segments Length」,点击生成按钮,会创建对应参数的圆柱
- 按F9呼出历史操作面板,修改参数后回车,旧圆柱会被替换成新参数的圆柱,同时主面板的属性值也会同步更新
- 回到主面板修改属性,再次点击生成按钮,历史面板也会显示最新的参数
这样就完全实现了你想要的动态修改属性的效果,不管是在主面板还是历史菜单调整参数,都能得到正确的结果。
备注:内容来源于stack exchange,提问作者Void
相关产品推荐
相关产品推荐

