本节摘要:插件偏好设置不是"记住用户上次勾了什么"的记忆片段,而是跨生命周期、多作用域、强一致性保障的元状态系统。Blender 强制所有偏好经由 AddonPreferences 类声明注册——这不是技术限制,而是契约式设计:插件遵守状态生命周期规则,Blender 提供跨会话持久化保障。本节拆解表达、存储、语义三层模型与全局状态维护的消息总线模式。
阅读完本节,你应当能够:
先回答一个根本问题:为什么 Blender 不允许插件自由读写 JSON 文件或注册全局变量来保存设置?
答案在设计基因里:Blender 是以确定性重放为底层信条的创作环境——一个工程文件应是自包含的时间胶囊,无论在何种硬件与版本下打开,状态演化路径完全可复现。若插件偏好游离于统一状态管理之外,用户把依赖特定配置的工程分享给同事时,对方看到的可能是功能残缺、参数错位的"幽灵现场"。
因此,偏好设置必须经由 AddonPreferences 声明注册。这是契约式设计的体现,三条核心条款:作用域隔离——每个插件的偏好仅对自身可见,与其他插件、核心设置严格隔离;序列化可控——所有属性可被自动序列化到用户配置,或导出为独立配置;变更可观测——任何属性修改可触发更新回调,实现"设置即逻辑"的响应式。
class MyAddonPrefs(bpy.types.AddonPreferences): bl_idname = __name__ # 必须是插件模块名 enable_batch_mode: bpy.props.BoolProperty( name="启用批量模式", description="开启批量处理与调试选项", default=False, ) cache_dir: bpy.props.StringProperty( name="缓存目录", subtype='DIR_PATH', default="", ) def draw(self, context): layout = self.layout layout.prop(self, "enable_batch_mode") layout.prop(self, "cache_dir") def register(): bpy.utils.register_class(MyAddonPrefs) # 任意位置的访问方式 prefs = bpy.context.preferences.addons[__name__].preferences
注意 bl_idname 必须写插件模块名——系统靠它定位你的偏好块。偏好类自带 draw 方法,用户在偏好设置的插件页就能看到你的设置界面,不用自己造面板。
⚠️ 常见坑:在偏好类上挂未声明的普通 Python 属性(如一个字典)当缓存。未通过属性工厂声明的成员不受 RNA 追踪,序列化时被忽略,偏好重建时还会引发意外行为。需要缓存,放到独立的模块级管理器里,别塞进偏好类。
还有一个隐蔽的时机问题:偏好实例在生命周期中可能被多次销毁重建——用户点"恢复默认值"、插件禁用再启用,都会触发。所以不要假设属性已按默认值初始化,用存在性校验接口判断"用户是否设置过",再决定是否执行首次初始化逻辑。
Blender 未规定插件只能用一种存储介质,而是允许按数据特性分层。
内置持久化:随工程文件走,适合"此工程默认使用的材质模板编号"这类项目强相关配置。载体是挂到 Scene 的属性组——第 2 章的主角。
用户级持久化:存于用户配置目录,跨所有项目生效,适合"启用实时预览"这类全局行为开关。载体就是 AddonPreferences。
外部持久化:插件自管的独立文件,适合大体积数据(日志、缓存索引)、需版本控制的配置、需与外部服务同步的令牌。位置用用户资源查询函数动态确定,跨平台安全。
三层不是互斥而是协同的镜像网络:一个渲染插件可以把模型权重路径存外部 JSON(不污染工程文件)、采样强度存用户级偏好(全局默认)、当前工程专用降噪阈值存工程内属性(项目特化),三者通过更新回调联动成统一视图。
| 数据特征 | 推荐层 | 载体 | 举例 |
|---|---|---|---|
| 随工程走 | 内置 | Scene 属性组 | 场景级工具参数 |
| 跨项目全局 | 用户级 | AddonPreferences | 行为开关、默认路径 |
| 大体积或需同步 | 外部 | 自管文件 | 模型权重、令牌、日志 |
| 只活在本次会话 | 不持久化 | WindowManager | 进度、面板状态 |
多上下文并发是常态:3D 视图、节点编辑器、后台渲染共享同一解释器,却各有上下文。插件的偏好可能在视图重绘时被读、在运算中改、在文件加载时重置。缺乏协调就是"竞态配置"——不同模块基于过期副本执行逻辑。
第一层防御是中心化状态代理:所有访问经由一个单例管理器路由,内置延迟绑定(每次获取最新实例)与缓存失效策略,避免高频重复访问:
class PrefsProxy: _cached = None @classmethod def get(cls): addon = bpy.context.preferences.addons.get(__name__) return addon.preferences if addon else None @classmethod def bool_flag(cls, key, default=False): prefs = cls.get() return getattr(prefs, key, default) if prefs else default
第二层防御是消息总线。Blender 内置的轻量事件总线允许订阅特定属性的变更:用户在偏好面板改了开关,总线把通知推给所有订阅者,插件据此刷新缓存、重启后台任务、重绘相关面板。状态维护从"轮询拉取"转向"事件驱动推送",不一致的窗口期被彻底消除。
def on_prefs_changed(*args): PrefsProxy._cached = None # 失效缓存,下次按需重取 # 此处可追加:重启后台任务、标记界面重绘 bpy.msgbus.subscribe_rna( key=(bpy.types.AddonPreferences, "enable_batch_mode"), owner=object(), # 持有者句柄,注销时用 args=(), notify=on_prefs_changed, )
一个健壮的插件,全局状态维护必然是三件套的融合:中心化代理管访问、消息总线管通知、更新回调管联动——闭环反馈系统,让状态变更的涟漪精准扩散到所有依赖方。
💡 关键直觉:问自己一个终极问题——"这个配置项,是用户意志的延伸,还是系统行为的约束?它该随项目迁移,还是扎根用户工作流?"答案直接决定它的存储层、界面呈现与封装粒度。没有银弹,只有基于场景的审慎权衡。

回头看这套体系施加的限制——必须继承基类、属性必须工厂声明、界面必须在 draw 里构建——看似束缚,实则是更高自由的基石。若允许随意创建全局变量,数百插件共存时命名空间污染、内存泄漏、版本冲突将野火蔓延;若允许直接操作底层结构,一次小版本升级就可能让整个生态崩溃。当前框架以"牺牲局部自由"换"系统级鲁棒"——让开发者无需成为内核专家,也能写出跨版本、跨平台、可维护的持久化逻辑。这与云原生架构中服务网格的思路异曲同工:不让你直接写协议栈,而是提供代理统一处理流量、加密、熔断;偏好框架正是插件生态的"状态网格"。
用"换机测试":用户换了台新电脑、打开一个老工程——此时还该存在的配置是偏好(跟着用户走),随工程而来的是场景参数(跟文件走),消失的是会话状态(本来就不该久留)。分不清时做这个思想实验,答案立现。典型错误是把"这个工程用哪种导出预设"存进偏好——用户换工程后偏好还是旧的,导出错版。
默认不能,但可以"曲线救国":把当前偏好导出成数据、存进场景的自定义属性或外部文件、随工程分发;对方的插件在加载时检测到随行的配置,提示"是否导入本工程的推荐设置"。这保持了用户主权的最终决定权(提示而非覆盖),又解决了配置随行的需求。直接改对方的偏好是越权行为,永不可取。
需要。订阅要传持有者句柄,插件注销时按句柄统一退订——否则你的回调在插件禁用后仍被调用,引用的模块早已过期。把所有订阅登记进一个列表、注销时遍历退订,与 1.4 节处理器清理是同一个纪律的不同面。
能,操作符可以放进偏好绘制里。但注意两点:按钮触发的操作符也要遵守准入契约(偏好界面也是一种上下文);耗时操作(网络测试)不能同步阻塞偏好窗口——用后台任务加状态轮询,把结果显示在旁边的只读属性上。偏好界面被卡住的感觉比主界面更糟,因为用户此刻的预期是"设置个软件而已"。
把视野拉远,偏好系统有两个值得预留接口的进化方向。
**团队配置分发。**工作室场景里,技术指导调好一套插件配置后,常需要推送给全组。为偏好加上"导出配置文件"与"从配置文件导入"两个操作符(走 4.3 节的原子写入与版本号),团队就拥有了配置的分发通道。进阶做法是加一个"启动时从指定路径检查团队配置"的选项——配置的治理从个人习惯升级为小组基础设施。
**配置的健康度。**偏好也是数据,也会有"过期与冲突":用户三年前设的默认路径早已不存在、两个互斥开关被同时打开。给偏好加一层校验(加载时检查路径存在性、互斥关系),把发现的问题以温和的方式提示(状态栏提醒而非弹窗轰炸),能消掉一类最难排查的"插件行为怪异"报告——根因不在代码,在陈旧配置。
这两个方向都不急于实现,但设计偏好结构时留出"可导出、可校验"的余地,将来就不必推倒重来。
收束成一份七条的最小清单,发布前逐条打勾:偏好类继承正确且模块名标识无误;每个属性都带名称描述与合理默认值;绘制方法排版分组清晰;访问统一走代理函数而非散装直取;变更联动挂了更新回调或总线订阅;注销时订阅全部退订;跨平台路径零硬编码。七条全绿,偏好模块即达到"用户无感"的标准——无感的含义是:设置永远记得、改了立刻生效、出问题能定位。偏好设置做得好的插件用户不会夸它,但做差的插件用户一定骂它,这类模块的价值就是沉默的可靠。
偏好管好了内务,下一节向外看:文件、网络与外部系统的数据交换。