Post

GSoC 2025 - Week 5

This week, I continued with the Annotations discussion in #889 and created PR #891 with the base framework needed for Annotations support.

This base framework allows user defined custom annotations. Even the default entity annotations part of MN will use the same framework. With this, any new annotation type will get the API interface and GUI support for free, which makes this very extensible. Blender’s Geometry Nodes are used as the underlying data store. They provide a lot more flexibility compared to regular Blender properties, especially when things are dynamic in nature.

Here is a video that shows this framework in action:

Here are some additional learnings this week:

  • Derived classes that don’t implement the abstract methods defined in the base class (using @abstractmethod) will still pass the hasattr check for that method. The abstract method cannot be executed error only shows up when the class is instantiated. To check if the derived class has the implementation of the method, the __qualname__ attribute of the method can be used to check which class provides the implementation
  • The mro() method (or the __mro__ attribute) can be used to get the ‘Method Resolution Order’ of a class’s methods or attributes. This is needed if we need to get all the python annotations of a class which includes ones defined in base classes as well
  • __getattr___ is only called when the normal mechanisms to locate the attribute fail. __getattribute__ is called all the the time and could have performance implications
  • The inspect module provides complete control to define parameters (using inspect.Parameter), their ordering etc to create a dynamic method signature (using inspect.Signature)
  • Dynamic classes can be created using the type() function by passing the name, any base classes and attribute / methods dictionary - we use this to return the dynamic annotation interface. MN also uses this mechanism to return the style interface
  • property() is a way to define managed attributes at a class level. Properties defined using property() cannot be assigned directly to instances. They can only be assigned to classes and will apply to all current and future instances. They work well with dynamic classes. The alternative to this would be to use __getattr__ and __setattr__ to handle all dynamic propety management
  • Panel placeholders in a Blender’s Geometry Node provide a way to add inputs so that when inputs are iterated, they are iterated in a required order. Dynamically adding a new socket at a specific position isn’t straightforward
  • Blender does not allow dynamic properties in an operator. To achieve dynamic properties in an operator (to show in the invoke UI for example), a temporary operator has to be created, registered with Blender and invoked. A dynamic PropertyGroup with dynamic properties can be created using the same type() method learnt above. This dynamic type can be used within a PointerProperty of the temporary operator for dynamic inputs
  • Similar to EXEC_DEFAULT learnt in the past for operators, EXEC_INVOKE can be used to directly call the operator and run it’s invoke method
  • Blender allows panels in the UI layout drawn by users (using layout.panel(...)). This allows better organization in what we display

Next week, I plan to work on the actual drawing of annotations using the gpu and blf Blender modules and create generic methods that can be used by all annotations.

This post is licensed under CC BY 4.0 by the author.