3
$\begingroup$

Can anyone help me confirm this is a bug, or possibly just something I might be doing incorrectly? It may also be the lack of a feature I'm assuming exists.

The problem is that add-on preferences only seem to get auto-saved when they are not within a property group branch. To replicate it, set this simple scenario up. You may already have something close enough - the properties do not need to be Boolean.

class PrefsSub(bpy.types.PropertyGroup):
    SkipTestB : bpy.props.BoolProperty( name = "Skip Test B" )

class PrefsMain(bpy.types.AddonPreferences):
    bl_idname = "PROJECT_NAME"
    SkipTestA : bpy.props.BoolProperty( name = "Skip Test A" )
    sub : bpy.props.PointerProperty(type=PrefsSub)

    def draw(self,context):
        area = self.layout.column()
        area.prop(self, "SkipTestA")
        area.prop(self.sub, "SkipTestB)
  1. Open Blender and Add-on preferences, modify Skip Test A, then close/open Blender. The SkipTestA state should remain intact, indicating that the property was automatically saved.
  2. Open Blender/prefs, modify Skip Test B, close/open Blender. If you encounter the same issue, the property does not get saved. Is this a bug?
  3. If you open Blender/prefs, then modify Skip Test A and Skip Test B, then close/open Blender, both properties appear to be saved.

It seems as if the auto-save triggering feature breaks down on property groups. Is this a known issue? And are there any workarounds for it? If this does not normally happen, can anyone guess something I may be doing incorrectly to cause it?

Really appreciate any knowledge about this. I have my preferences setup to use multiple property groups because I didn't realize this happened until it was too late. I can probably flatten the hierarchy, but would prefer to not do that if possible.

Edit: Some info and possible solution

  • Forcing a root preferences value (prefs.prop) to change when a property group value (prefs.group.prop) changes does not trigger an auto-save.
  • In fact, programmatically altering root preference values does not seem to trigger an auto-save at all. Which would mean states stored in prefs won't get automatically saved unless the user modifies a root preference value using the UI within the same Blender session.
  • When a root preferences value is modified by the user through the UI, Blender will save all add-on preferences on exit - even those in property groups.
  • My current solution is to add update functions to all preference properties, and have the update function set bpy.context.preferences.use_preferences_save = True.
  • The solution I'm going with at the moment is to set bpy.context.preferences.use_preferences_save = True when my add-on starts up. Read about this state here: https://docs.blender.org/api/current/bpy.types.Preferences.html?highlight=use_preferences_save#bpy.types.Preferences.use_preferences_save . According to documentation, setting this to True will trigger modified preferences to be saved on exit. In my opinion, it seems like this should always happen.
  • If anyone can think of a better solution, please let me know! I personally do not like forcing this value to be True on every Blender execution, but it seems like the simplest solution. I guess the best solution would be to avoid property groups altogether in preferences, and to only trigger an auto-save when procedurally changing preference properties internally. But since my add-on does this every time it executes, it makes sense to do it the lazy way.
$\endgroup$
4
  • 2
    $\begingroup$ Suggest if you want people to test this, then make the script a minimal working addon, with bl_info and registration. Haven't noticed this behaviour. $\endgroup$
    – batFINGER
    Commented Nov 10, 2019 at 2:34
  • $\begingroup$ Yeah, I wanted to do that, but I don't have enough experience to throw one together. I've only worked with Python/Blender through a single multi-file project that is too convoluted to use as a test. Plus I figured quite a few add-ons in development would already have a setup very similar to the one above. $\endgroup$
    – Robert
    Commented Nov 10, 2019 at 14:08
  • $\begingroup$ I think I've confirmed this is happening to at least several other people. While researching solutions, I keep running into posts about preferences not being saved correctly, and the poster has them in property groups. Example: devtalk.blender.org/t/… $\endgroup$
    – Robert
    Commented Nov 10, 2019 at 15:57
  • $\begingroup$ @Robert you can use the Templates menu in the script editor to scaffold an add-on, you don't need to know how to do it from scratch $\endgroup$
    – Moog
    Commented Nov 17, 2019 at 1:39

1 Answer 1

1
$\begingroup$

Based on the updates to the question and the comments, I am turning the solution into an answer so others can more easily find it.

I have a UI in the N panel, and every time I would change the setting the preferences would get updated and I could use them elsewhere with no errors. But just like you, it wasn't saving after I closed Blender unless I made the change directly from Preferences -> Add-ons.

The solution was to set bpy.context.preferences.use_preferences_save to true. For convenience, I made that setting change in the register() function. These are the relevant parts of my code that demonstrate how to do it.

# __init__.py

bl_info = {
   # Add-on info...
}

import bpy

class MyPreferences(bpy.types.AddonPreferences):
    bl_idname = __package__

    my_port: bpy.props.IntProperty(
        name="Port",
        min=0,
        max=65535,
        default=5678
    )

def get_value(self):
    return bpy.context.preferences.addons[__package__].preferences.my_port

def set_value(self, value):
    bpy.context.preferences.addons[__package__].preferences.my_port = value

class MyPanel(bpy.types.Panel):
    """This is a menu within the N panel"""
    bl_label = "Settings"
    bl_idname = "OBJECT_PT_MyPanel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'My Panel'

    bpy.types.Scene.my_port = bpy.props.IntProperty(
        name="Server Port",
        min=0,
        max=65535,
        get=get_value,
        set=set_value
    )

    def draw(self, context):
        _prefs = bpy.context.preferences.addons[__package__].preferences
        layout = self.layout
        row = layout.row()
        row.prop(context.scene, "my_port")

def register():
    bpy.utils.register_class(MyPreferences)
    bpy.utils.register_class(MyPanel)
    bpy.context.preferences.use_preferences_save = True # THIS IS THE MAGIC LINE


def unregister(): 
    bpy.utils.unregister_class(MyPreferences)
    bpy.utils.unregister_class(MyPanel)

if __name__ == "__main__":
   register()

I agree that this should be a default value, but alas... 'tis not.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .