Avoiding Blender's Relative paths

Tutorials Oct 4, 2016

Blender uses a special kind of relative paths. These paths start with ”//” and are relative to the blend file they are set in. The double dashes replace the path to the blend file’s directory, or in the case of library objects they replace the full path to the file.

The problem with using these paths when scripting is that the rest of Python doesn’t understand them. If you pass them to something like Popen you would get permission or file not found errors.

This is a special pain when using String Properties in addon preferences. You can set the subtype in them to FILE_PATH to get a nice file browser button, but there’s no way to avoid getting relative paths. Worse yet, they are relative to the current blend file. Since it’s impossible to figure out from which file they were set originally, you would be unable to resolve that path in the future.

The user can make the file browser return absolute paths by ticking the “relative” checkbox in the left panel. But why not just enforce them and save us some time and trouble?

Let’s make a callback

We can use the update callback to force absolute paths. First we want to write a generic function to do the conversion so we can plug our properties into it.

# Don't forget to import the os module
import os
import bpy

def make_path_absolute(key):
    """ Prevent Blender's relative paths of doom """
    
    # This can be a collection property or addon preferences
    props = bpy.context.scene.my_collection
    sane_path = lambda p: os.path.abspath(bpy.path.abspath(p))

    if key in props and props[key].startswith('//'):
        props[key] = sane_path(props[key]) 

Unfortunately Blender doesn’t make this easy. You don’t get a reference to the property that’s triggering the update, so we’ll have to pass it manually.

This function exploits a small implementation detail in the api. If you set a property’s value using array notation (square brackets) you won’t re-trigger any callbacks. On the other hand dot notation (prefs.key) triggers callbacks and causes infinite recursion.

The sane_path lambda is just there to make things more readable (it’s a matter of taste :) ). The bpy.path.abspath function from will take care of the ’//’ replacing it with the full path to the blend directory, while os.path.abspath will take care of any other relative ’../’ that might still be there.

As I mentioned earlier, we can’t use that function directly since it needs to receive a reference to the property being updated. Our actual update callback will be an anonymous function which calls make_path_sane with the property’s name.

    my_filepath = StringProperty(
        name = 'Absolute filepath',
        update = lambda s,c: make_path_absolute('my_filepath'),
        subtype = 'FILE_PATH',
    )

Notice that the update callback takes two parameters, self (s) and context (c). I don’t use them, but you could pass them too if you wanted.

Here’s the code at work in Render+’s addon preferences: Example from Render+

If you want to know more the Blender manual has a section about relative paths

Tweet CC-BY-NC-SADiego Gangl

Subscribe now and be the first to read the new articles!

Hosting by XMundo Networks
comments powered by Disqus