Addons for 2.66..when where and how to start, continue and stop storing things

In the newest releases (somewhere around 2.65) there was a big change which prevents addons from accessing context or blend data at registration. After reading through the [BF-Python] emails, I’m of the opinion the changes are for the better despite my feelings getting hurt by really broken add-ons. Let’s discuss some good and bad practices, work arounds and scenarios…in the hopes of improving our brains, and improving our add-ons.

I’ll start by positing a few scenarios which I can imagine some python add-on dev’s saying out loud…

Scenario 1:
I have an add-on which different people will use slightly differently. I would like them to be able to store some settings locally on their machine so each time they start blender, these settings are the same and my add-on behaves to their liking. When, where, and how, do I start, continue and stop those settings?

Example: the size in pixels of a pie menu radius will be different based on peoples eye-sight, screen resolution and screen size.

Scenario 2:
I have some arbitrary variable which is going to change while the user interacts with my add-on and their blend data. It only needs to be around while blender and my add-on are running so I can keep track of something. Eg, I’d like to keep up with something throughout a workflow. When blender closes, I’m no longer interested in it. If a user sends their file to someone else with my addon, that variable does not need to come along. When, where, and how, do I start, continue and stop using that variable?

Example: I wan’t to keep track of how many times a particular operator has been used since my add-on initialized.

Scenario 3:
My add-on has some resources it uses, and they need to be in bpy.data for me to really use them. I wan’t them locked and loaded ready to go after my addon starts. If I wait until the user calls a function to load it, it’s going to really slow down that first user interaction. However, I dont need them saved to any .blend files because they are only used by my addon. When, where, and how, do I start, continue and stop loading those resources? We don’t want them in bpy.data because if a new blend file is opened…our resources will go away.

Example. An image is used to draw with bgl. The image needs to be in bpy.data.images so the built-in gl_load() function can be used.

I’ve seen all these tackled by stashing properties in bpy.context.scene when add-ons registers. Or loading/linking/appending resources out of blend files in the add-on directory(Mackrakens material library). Now those things are out of the question at add-on registration.

Time to show and tell how you all have tackled these problems or others related to the new API, cleanly or not. Some best practice advice would be great too.

Scenario 1: Use the new AddonPreferences


Scenario 2: Maybe register an app.handler? If you disable your addon and the unregister removed the handler, it will stop the callbacks. Variable itself should be registered like bpy.types.*.var = bpy.props.Property() and removed in unregister function like del bpy.types..var
Also see: http://wiki.blender.org/index.php/Dev:2.6/Source/Render/UpdateAPI

Or a modal operator, that constantly runs in the background (shouldn’t update every 0.25 seconds, but 0.75 or higher). You might have to click a button to start it, as the op could access the context instantly, so you can’t auto-start within register()

Scenario 3: bpy.data should still be accessible, so why not create/load an image with low-level function calls?

btw. if you need a property but don’t want it to be saved in blend, pass this:
BoolProp(…, options={‘SKIP_SAVE’})

Thanks Codemax, you hit all of them in one post! I was picturing more of a discussion w/ different example from lots of different people to see some different “styles” where people have tackled things.

A different approach for scenario #2 was suggest to me by Ideasman by defining a global variable in the addon namespace. I had to take a minut to digest that suggestion because my programming fundamentals are a little soft, but once I get a true grasp on the concepts, I’ll try and post an example.

so you want me to post code snippets, eh? You gotta wait until i finished my work for university :wink:

as for global vars, you could try something like:

def your_func()
    global your_var
    print(your_var)

your_var = "I am globaled"
your_func()

http://www.python-course.eu/global_vs_local_variables.php

I have same problem , trying to access bpy.data.images . Is there documentation anywhere for the new restrictions ?

bpy.data.* shouldn’t be affected, unless operators are involved.

I can’t find the new type RestrictedContext in API, should actually be there since last docs rebuild…

Actually it does, here is the commit in more detail

http://markmail.org/thread/6uesq4rguwiesmfe

and here is my error

File "/Users/kilon/blender-build/install/darwin/blender.app/contents/macos/2.65/scripts/modules/addon_utils.py", line 269, in enable
    mod = __import__(module_name)
  File "/Users/kilon/blender-build/install/darwin/blender.app/contents/macos/2.65/scripts/addons/pyEphestos/__init__.py", line 165, in <module>
    textured_morph.set_texture( "swatch.png" )
  File "/Users/kilon/blender-build/install/darwin/blender.app/contents/macos/2.65/scripts/addons/pyEphestos/morpheas/morph.py", line 72, in set_texture
    self.texture= bpy.data.images.load(filepath = file_path)
AttributeError: '_RestrictData' object has no attribute 'images'


Yep bpy.data is also restricted. Also for good reason (we can argue that later :slight_smile: ) So, if your addon needs to load resources, you are stuck until after addon is registtered/enabled. However, here is my (elegant? ugly?) solution.

persistent save_pre, save_post and load_post handlers. Here is what we did.

  1. Make a some kind of property to know whether resource is loaded. We used a Bool property in our PropertyGroup, but this is tied to the Scene…so it gets saved in blend data. Therefore it is imperative that our handlers (see below) handle this property appropriately. Alternatively, I think a global bool property will be persistent across file loads/saved…either way, the bookkeeping has to be immaculate…which I’m not very good at.

  2. Define the persistent handlers

-save_pre : remove image (or any other) resources so it doesn’t interfere with user’s .blend
-load_post: load the image (or other) resources so your addon can start working
both of these read and modify the “resource loaded” property

Here are the code samples directly from pie_menus addon. I’ll edit this to have generic code when I get the time.
https://bitbucket.org/liquidape/blender-pie-menus/src/030f53a902405118a3c6c34eafb17cfdd28f864e/piemenus/init.py?at=master#cl-442

  1. Register the handlers AFTER you have registered your property

https://bitbucket.org/liquidape/blender-pie-menus/src/030f53a902405118a3c6c34eafb17cfdd28f864e/piemenus/init.py?at=master#cl-491

interesting solution, thanks!

But does it mean that you need to save or load a .blend first before the addon starts working if you add and enable one at runtime?
(like you copy the addon file into addon dir while blender is running, F8 and enable the addon, no save/load events fired?)

Anyway, seems like a clean solution to me, ugly would be:

  1. add a scene_update_pre handler at the bottom of register()
  2. let that handler load the resources
  3. remove the handler inside of the handler function, this way it should execute only once

This actually worked in a test, but may depend on timing and could therefore randomly fail…

Very Good question…I knew I was forgetting something in the last post. No, it will not work…in fact, even if the addon is enabled by defualt. You need to add in a utility in your functions to see if things are loaded. For this there are 2 options

Opions:
-A) “Start” button of some kind that the user has to click (eeew, gross)
-B) Every function that the user could conceievably call first which requires the resrouce will need to check for resrouce availability and load it if not there. Also not good, and it makes the first function SLOW

For piemenus, we went with B…it kinda stinks. lots of complaints about first menu being slow. Here is the block of code which is in the init of our menu class. Whic, I’m glad you made me look at it, I can replace that whole block of code with my load_post() function.

https://bitbucket.org/liquidape/blender-pie-menus/src/d5a1702b50e37e33bc233a24f697dc85d3d69b62/piemenus/pie_menu.py?at=master#cl-330