The last couple of weeks I have been porting almost a hundred add-ons, scripts and snippets to Blender 2.9x and you would think that to be a lot of work. This proved to be not the case though, because the changes between 2.8x and 2.9x are actually fairly minimal and even scripts dating back all the way to 2.78 were fairly straightfoward to port.
This doesn't mean there were no changes but the fundamental concepts in the Blender python API, like data access and the way operators and panels work stayed the same. What changed were mainly additions (new operators, other new classes like all kinds of nodes), minor changes (optional arguments must now be passed using the keyword) and renaming (groups became collections, lamps became lights).
There were some exceptions though that had more impact, for example, the decision to require annotations for properties and force a naming convention onto classes that need to be registered (like Panel and Menu derived classes) force you to check every add-on because although not mandatory yet, it will be in the future.
Another change that required a bit of work was that the helper function to register all classes in a module has been removed. As we will see in the examples, an alternative is provided.
The biggest change has been in the OpenGL bindings, but although this has a lot of impact, only a minority of add-ons deal with OpenGL drawing.
Anyway, in the list of examples below, I have highlighted the changes, sorted roughly based on how many scripts it has affected (I think).
Required version in bl_info
bl_info["blender"]
is no longer just a minimum but must list a version that is at least 2, 80, 0. Otherwise your add-on simply won't run, even if it were compatible.
bl_info = { "name": "Some Operator", "author": "Me, myself and I", "version": (1, 2, 3), "blender": (2, 78, 0), "location": "View3D > Object > Some Op", "description": "An operator doing someting", "category": "Special stuff"}new:
bl_info = { "name": "Some Operator", "author": "Me, myself and I", "version": (1, 2, 4), "blender": (2, 92, 0), "location": "View3D > Object > Some Op", "description": "An operator doing someting", "category": "Special stuff"}
register_module() no longer exists
The way classes that need to be registered are dealt with has changed. Instead of registering everything in a module we need to register individual classes.
from bpy.utils import register_module, unregister_module def register() register_module(__name__) def unregister() unregister_module(__name__)
There is now a factory fumction for this(docs)
from bpy.utils import register_classes_factory classes = [Myoperator, VIEW3D_PT_mypanel] register_classes, unregister_classes = register_classes_factory(classes) def register(): register_classes() def unregister(): unregister_classes()
properties are no longer assigned values but annotated
I never understood the benefits of this change but it affects almost every add–on
Old:
class Myoperator(bpy.types.Operator): bl_idname = 'mesh.myoperator' bl_label = 'Do something' bl_options = {'REGISTER', 'UNDO'} someprop = IntProperty(name="Some prop")
New: Note that the only visible difference is that we now use a colon (:)
class Myoperator(bpy.types.Operator): bl_idname = 'mesh.myoperator' bl_label = 'Do something' bl_options = {'REGISTER', 'UNDO'} someprop : IntProperty(name="Some prop")
add-on preferences class needs to be registered
This wasn't the case earlier, but it is as simple as adding it to your list of classes
class MyAddonPrefs(bpy.types.AddonPreferences): bl_idname = __name__ # give settings the name of the python module somepref : IntProperty(name="Some pref") def draw(self, context): layout = self.layout layout.prop(self, "somepref") classes = [Myoperator, MyAddonPrefs] register_classes, unregister_classes = register_classes_factory(classes)
Python 3.7
The visible bit of moving to a newer python version is mainly the use of the @ operator instead of *
mat = ob.matrix_world.inverted() for vert in ob_verts: world_coordinates = mat * vert.co
mat = ob.matrix_world.inverted() for vert in ob_verts: world_coordinates = mat @ vert.co
This is also applies to multiplying two vectors: * is now element-wise multiplication, @ gives the dot product (this is in line with Numpy)
Of course you get all the added benefits from Python 3.7 as well although most are not relevant for add-on development per se.
renamed built-in icons
Not only have the icons been redesigned, many new ones are available and quite a few are removed. The list of change sis to big to list here but if you want an overview of the changes since 2.78 I have made a page
Name change for some types of objects
Lamp
has become Light
and Group
has become Collection
. Collections offer a lot more functionality too of course but at its simplest level a collection is just a group.
mandatory keyword parameters
Functions that have optional parameters not must use a keyword. Some notable examples
setting text in an area header,
for example when displaying the status of a modal operator
(docs)
context.area.header_text_set(text="something") context.area.header_text_set(text=None)
And the label()
function in a layout (docs),
which is used often in panels and operator draw functions.
layout.label(text="something")
layout.split()
Talking about layout, a minor change is that the percentage
argument renamed to factor
(docs)
layout,split(factor=0.40)
A scene has now a separate cursor attribute
See here
x,y,z = context.scene.cursor.location
Vertex colors are now RGBA
But the Color()
constructor always expects 3 values (docs)
Which means you cannot assign a Color object to a .color attribute in a vertex color layer.
some methods expect a dependency graph
The ray_cast() function for example
so instead of
scene.ray_cast(origin, direction )
scene.ray_cast(context.window.view_layer.depsgraph, origin, direction )
The mathutils.bvhtree.BVHTree.FromObject function also needs it.
user preferences are now called preferences
Still accessible through the context
but now as ,code>context.preferences instead of context.user_preferences
Note that this breaks stored preferences as well since they might contain references to this attribute in the set itself (preferences are stored as executable python)
The active object is accessed differently
scene.objects.active
is no longer available, use context.active_object
(docs)
To set an object as the active object you need to do something different as well
context.view_layer.objects.active = myobj
Selecting objects using setters/getters
instead of
ob.select = True
ob.select_set(True)
More here
There is a select_get()
as well.
This is consistent with selection functions in BMeshes for verts, edges, etc. (docs)
Menus have been shuffled
For example the Object and Mesh menus were part of the Infobar but are now part of the 3d view area.
So, for example INFO_MT_mesh_add menu
is now called VIEW3D_MT_mesh_add
The info bar is no longer present by default, the part at the very top (with File, Edit, Render, ...) is now called the TOPBAR
attributes have changed on the Subsurface modifier
Notably the way to perform uv subdivision as well see more here
mod.use_subsurf_uv = True
is no longer needed.
OpenGL bindings are completely changed
bgl
does no longer support direct mode at all (not even as an option).
Which means everything has to be done replaced by shaders. more here