Tutoriel sur les add-ons

Audience visée

Ce tutoriel est conçu pour aider les artistes techniciens ou les développeurs à apprendre à étendre Blender. Une compréhension des bases de Python est exigée de ceux qui mettent en œuvre ce tutoriel.

Prérequis

Avant de poursuivre ce tutoriel, vous devriez…

  • Avoir une connaissance des bases de travail dans Blender.
  • Savoir comment lancer un script dans l’Éditeur de texte de Blender.
  • Avoir une compréhension des types de primitives de Python (int, boolean, string, list, tuple, dictionary, et set).
  • Avoir une connaissance du concept des modules Python.
  • Avoir une compréhension de base des classes (orientation objet) dans Python.

Lectures suggérées avant de commencer ce tutoriel.

Pour mieux trouver des solutions à tout message d’erreur qu’envoie Python pendant l’écriture des scripts, vous lancez Blender depuis un terminal. Voir :ref:`Utilisation du Terminal <https://docs.blender.org/api/blender_python_api_current/info_tips_and_tricks.html#use-the-terminal>`__.

Qu’est-ce qu’un add-on ?

Un add-on est un module Python avec quelques exigences supplémentaires, ainsi Blender peut l’afficher dans une liste avec des informations utiles.

En guise d’exemple, voici l’add-on le plus simple possible

bl_info = {
    "name": "My Test Add-on",
    "blender": (2, 80, 0),
    "category": "Object",
}
def register():
    print("Hello World")
def unregister():
    print("Goodbye World")
bl_info
est un dictionnaire contenant des métadonnées de l’add-on telles que le titre, la version et l’auteur à afficher dans la liste des add-ons des Preferences. Il spécifie également la version minimale de Blender requise pour exécuter le script ; les versions plus anciennes ne vont pas afficher l’add-ondans la liste.
register
est une fonction qui se lance seulement une fois l’add-on activé, ce qui signifie que le module peut être chargé sans activation de l’add-on.
unregister
est une fonction pour décharger tout ce qui a été installé par register, celle-ci est appelée quand l’add-on est désactivé.

Notez que cet add-on ne fait rien concernant Blender (le module bpy n’est pas importé par exemple).

Ceci est un exemple artificiel d’un add-on qui sert à illustrer le fait que les exigences de base d’un add-on sont simples.

Un add-on typiquement enregistrera des opérateurs, des panneaux, des éléments de menu, etc, mais il convient de noter que n’importe quel script peut faire cela, quand il a été exécuté depuis l’éditeur texte ou même depuis la console interactive – il y a intrinsèquement aucune différence avec un add-on qui s’autorise à s’intégrer dans Blender, une telle fonctionnalité est simplement offerte par le module bpy pour que n’importe quel script puisse y accéder.

Aussi un add-on est simplement une manière d’encapsuler un module Python de manière à ce qu’un utilisateur puisse aisément l’utiliser.

Note

L’exécution de ce script dans l’éditeur texte n’affichera rien, pour voir le résultat il doit être installé via les Preferences. Des messages seront affichés à son activation et à son désactivation.

Votre premier add-on

L’add-on le plus simple possible ci-dessus est utile comme exemple mais pas plus. L’add-on suivant est simple mais montre comment intégrer un script dans Blender en utilisant un Operator qui est la manière typique de définir un outil accessible depuis les menus, boutons et raccourcis clavier.

Pour le premier exemple, nous allons écrire un script qui déplace simplement tous les objets dans une scène.

Écrire le script

Ajoutez le script suivant à l’éditeur de texte de Blender

import bpy

scene = bpy.context.scene
for obj in scene.objects:
    obj.location.x += 1.0

Cliquez sur le bouton Run Script, tous les objets dans la scène active sont déplacés de 1.0 nité.

Écrire l’add-on (simple)

Cet add-on prend le corps du script ci-dessus, et l’ajoute à la fonction execute() d’un opérateur.

bl_info = {
    "name": "Move X Axis",
    "blender": (2, 80, 0),
    "category": "Object",
}

import bpy


class ObjectMoveX(bpy.types.Operator):
    """My Object Moving Script"""      # Use this as a tooltip for menu items and buttons.
    bl_idname = "object.move_x"        # Unique identifier for buttons and menu items to reference.
    bl_label = "Move X by One"         # Display name in the interface.
    bl_options = {'REGISTER', 'UNDO'}  # Enable undo for the operator.

    def execute(self, context):        # execute() is called when running the operator.

        # The original script
        scene = context.scene
        for obj in scene.objects:
            obj.location.x += 1.0

        return {'FINISHED'}            # Lets Blender know the operator finished successfully.

def register():
    bpy.utils.register_class(ObjectMoveX)


def unregister():
    bpy.utils.unregister_class(ObjectMoveX)


# This allows you to run the script directly from Blender's Text editor
# to test the add-on without having to install it.
if __name__ == "__main__":
    register()

Note

bl_info est réparti entre plusieurs lignes. C’est simplement une convention de style pour ajouter plus facilement des éléments.

Note

Plutôt que d’utiliser``bpy.context.scene``, nous utilisons l’argument context.scene passé à``execute()``. Dans la plupart des cas, ceux-ci seront les mêmes. Cependant, dans certains cas, un contexte personnalisé sera passé aux opérateurs aussi les auteurs de scripts devraient préférer l’argument context passé aux opérateurs.

Pour tester le script, vous pouvez le copier et le coller dans l’éditeur Texte de Blender et le lancer. Ceci va exécuter le script directement et appeler register immédiatement.

Cependant le lancement du script ne va pas déplacer les objets. Pour cela, vous devez exécuter l’opérateur nouvellement enregistré.

../../_images/advanced_scripting_addon-tutorial_operator-search-menu.png

Menu Operator Search.

Faites cela en pressant F3 pour afficher le menu operator search et tapez « Move X by One » (le bl_label), puis Entrée.

Les objets devraient se déplacer comme auparavant.

Gardez cet add-on ouvert dans Blender pour la prochaine étape - installer.

Installer l’add-on

Une fois que vous avez votre add-on dans l’Éditeur de texte de Blender, vous allez souhaiter l’installer de sorte qu’il puisse être activé dans les Preferences pour se lancer au démarrage.

Même si l’add-on ci-dessus est un test, parcourons néanmoins les étapes et ainsi vous saurez comment faire pour plus tard.

Pour installer le texte Blender comme un add-on, vous devrez d’abord l’enregistrer sur disque. Veuillez suivre les restrictions de nommage qui s’appliquent aux modules Python dont une extension .py.

Une fois le fichier sur disque, vous pouvez l’installer comme vous le feriez pour un add-on téléchargé.

Ouvrez le Preferences ‣ Add-ons ‣ Install… et sélectionnez le fichier.

Maintenant l’add-on sera listé et vous pourrez l’activer en pressant la case à cocher, si vous voulez l’activer au redémarrage, pressez Save as Default.

Note

La destination d’un add-on dépend de votre configuration de Blender. Pendant l’installation d’un add-on les chemins de la source et de la destination sont affichés sur la console. Vous pouvez aussi trouver les emplacements des chemins de l’add-on en lançant ceci dans la console Python.

import addon_utils
print(addon_utils.paths())

Plus sur ce sujet ici structure des dossiers.

Votre second add-on

Pour notre second add-on, nous nous focaliserons sur l’instanciation d’objet – ceci est – pour faire des copies liées d’un objet d’une façon semblable à ce que vous avez pu voir avec le modificateur Array.

Écrire le script

Comme précédemment, d’abord nous allons commencer par un script, le développer, puis le convertir en un add-on.

import bpy
from bpy import context

# Get the current scene
scene = context.scene

# Get the 3D cursor location
cursor = scene.cursor.location

# Get the active object (assume we have one)
obj = context.active_object

# Now make a copy of the object
obj_new = obj.copy()

# The new object has to be added to a collection in the scene
scene.collection.objects.link(obj_new)

# Now we can place the object
obj_new.location = cursor

Maintenant essayons de copier ce script dans Blender et le lancer sur le Cube par défaut. Assurez-vous que vous avez déplacé le curseur 3D avant le lancement, étant donné que la copie va apparaître à l’emplacement du curseur.

Après le lancement, notez que lorsque vous passez en Mode Édition pour modifier le Cube – toutes les copies changent. Dans Blender, ce point est connu comme Copies liées.

Ensuite nous allons faire cela dans une boucle, pour faire un tableau (array) d’objets entre l’objet actif et le curseur.

import bpy
from bpy import context

scene = context.scene
cursor = scene.cursor.location
obj = context.active_object

# Use a fixed value for now, eventually make this user adjustable
total = 10

# Add 'total' objects into the scene
for i in range(total):
    obj_new = obj.copy()
    scene.collection.objects.link(obj_new)

    # Now place the object in between the cursor
    # and the active object based on 'i'
    factor = i / total
    obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

Essayez de lancer ce script avec l’objet actif et le curseur à distance l’un de l’autre pour voir le résultat.

Avec ce script vous allez remarquer que nous sommes en train de faire des maths avec la position de l’objet et le curseur, ceci fonctionne car les deux sont des instances 3D de mathutils.Vector, une classe pratique fournie par le module mathutils qui permet la multiplication des vecteurs par de nombres et des matrices.

Si vous êtes intéressé par ce point, lisez mathutils.Vector – il y a beaucoup de fonctions utilitaires pratiques telles que l’obtention de l’angle entre deux vecteurs, le produit vectoriel, le produit scalaire ainsi que des fonctions plus avancées dans mathutils.geometry telles que l’interpolation de courbe de Bézier et l’intersection rayon/triangle.

Pour l’instant nous allons nous focaliser à faire de ce script un add-on, mais il est bon de savoir que ce module de math 3D est disponible et peut vous aider avec une fonctionnalité plus avancée plus tard.

Écrire l’add-on

La première étape est de convertir le script tel quel en un add-on:

bl_info = {
    "name": "Cursor Array",
    "blender": (2, 80, 0),
    "category": "Object",
}

import bpy


class ObjectCursorArray(bpy.types.Operator):
    """Object Cursor Array"""
    bl_idname = "object.cursor_array"
    bl_label = "Cursor Array"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        scene = context.scene
        cursor = scene.cursor.location
        obj = context.active_object

        total = 10

        for i in range(total):
            obj_new = obj.copy()
            scene.collection.objects.link(obj_new)

            factor = i / total
            obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

        return {'FINISHED'}

def register():
    bpy.utils.register_class(ObjectCursorArray)


def unregister():
    bpy.utils.unregister_class(ObjectCursorArray)


if __name__ == "__main__":
    register()

Tout ici a été couvert dans les étapes précédentes, il se peut que vous vouliez encore essayer de lancer l’add-on et considérer ce qui pourrait être fait pour le rendre plus utile.

Les deux choses les plus évidentes qui manquent sont – avoir le total fixé à 10, et avoir l’accès à l’opérateur depuis la barre d’espace n’est pas très pratique.

Ces deux ajouts sont expliqués ci-après, avec le script final après.

Propriété d’opérateur

Il y a une diversité de types de propriétés qui sont utilisés pour les réglages d’outils, les types de propriété habituels comprennent : int, float, vector, color, boolean et string.

These properties are handled differently to typical Python class attributes because Blender needs to display them in the interface, store their settings in keymaps and keep settings for reuse.

Bien que ceci soit géré de manière clairement Pythonique, soyez conscient que vous êtes en fait en train de définir des réglages d’outils qui sont chargés dans Blender et accessibles par d’autres parties de Blender, en dehors de python.

Pour nous passer du littéral 10 pour total, nous allons utiliser une propriété d’opérateur. Les propriétés d’opérateur sont définies via le module bpy.props, ceci est ajouté au corps de la classe:

# moved assignment from execute() to the body of the class...
total: bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)

# and this is accessed on the class
# instance within the execute() function as...
self.total

Ces propriétés de bpy.props sont gérées spécialement par Blender quand la classe est enregistrée de sorte qu’elles s’affichent comme des boutons dans l’interface utilisateur. Il y a un grand nombre d’arguments que vous pouvez passer aux propriétés pour définir des limites, modifier les valeurs par défaut et afficher une info-bulle.

Voir aussi

bpy.props.IntProperty

Ce document n’entre pas dans les détails sur l’utilisation d’autres types de propriétés. Cependant le lien ci-dessus comprend des exemples plus avancés d’utilisation de propriété .

Configuration de clavier

In Blender, add-ons have their own keymaps so as not to interfere with Blender’s built-in keymaps.

In the example below, a new object mode bpy.types.KeyMap is added, then a bpy.types.KeyMapItem is added to the keymap which references our newly added operator, using Shift-Ctrl-T as the key shortcut to activate it.

# store keymaps here to access after registration
addon_keymaps = []

def register():

    # handle the keymap
    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')

    kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'T', 'PRESS', ctrl=True, shift=True)
    kmi.properties.total = 4

    addon_keymaps.append((km, kmi))


def unregister():

    # handle the keymap
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()

Remarquez comment l’élément de configuration de clavier peut avoir un réglage total différent de celui par défaut défini par l’opérateur. Ceci vous permet d’avoir de multiples clés d’accès au même opérateur avec des réglages différents.

Note

Alors que Shift-Ctrl-T n’est pas une touche de raccourci de Blender par défaut, il est difficile de s’assurer que les add-ons ne vont pas remplacer leurs configurations de touches de clavier respectives. Au moins soyez attentifs à l’assignation des touches de sorte qu’elles ne rentrent pas en conflit avec une fonctionnalité importante dans Blender.

Pour la documentation de l’API sur les fonctions listées ci-dessus, voir :

En les combinant tous ensemble

bl_info = {
    "name": "Cursor Array",
    "blender": (2, 80, 0),
    "category": "Object",
}

import bpy


class ObjectCursorArray(bpy.types.Operator):
    """Object Cursor Array"""
    bl_idname = "object.cursor_array"
    bl_label = "Cursor Array"
    bl_options = {'REGISTER', 'UNDO'}

    total: bpy.props.IntProperty(name="Steps", default=2, min=1, max=100)

    def execute(self, context):
        scene = context.scene
        cursor = scene.cursor.location
        obj = context.active_object

        for i in range(self.total):
            obj_new = obj.copy()
            scene.collection.objects.link(obj_new)

            factor = i / self.total
            obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator(ObjectCursorArray.bl_idname)

# store keymaps here to access after registration
addon_keymaps = []


def register():
    bpy.utils.register_class(ObjectCursorArray)
    bpy.types.VIEW3D_MT_object.append(menu_func)

    # handle the keymap
    wm = bpy.context.window_manager
    # Note that in background mode (no GUI available), keyconfigs are not available either,
    # so we have to check this to avoid nasty errors in background case.
    kc = wm.keyconfigs.addon
    if kc:
        km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
        kmi = km.keymap_items.new(ObjectCursorArray.bl_idname, 'T', 'PRESS', ctrl=True, shift=True)
        kmi.properties.total = 4
        addon_keymaps.append((km, kmi))

def unregister():
    # Note: when unregistering, it's usually good practice to do it in reverse order you registered.
    # Can avoid strange issues like keymap still referring to operators already unregistered...
    # handle the keymap
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()

    bpy.utils.unregister_class(ObjectCursorArray)
    bpy.types.VIEW3D_MT_object.remove(menu_func)


if __name__ == "__main__":
    register()
../../_images/advanced_scripting_addon-tutorial_in-menu.png

Dans le menu.

Lancer le script (ou enregistrez-le et ajoutez-le aux Preferences comme auparavant) et il apparaîtra dans le menu Object.

../../_images/advanced_scripting_addon-tutorial_op-prop.png

Propriété d’opérateur.

Après l’avoir sélectionné dans le menu, vous pouvez choisir combien d’instances du cube vous voulez créer.

Note

L’exécution directe du script à plusieurs reprises va également ajouter le menu à chaque fois. Bien que ce ne soit pas un comportement utile, il n’y a rien à craindre puisque les add-ons ne vont pas enregistrer eux-mêmes plusieurs fois une fois activé via les Preferences.

Conclusions

Les add-ons peuvent encapsuler certaines fonctionnalités pour l’écriture d’outils pour améliorer votre flux de travail ou pour écrire des utilitaires pour les autres.

Bien qu’il y ait des limites à ce que Python peut faire dans Blender, il y a certainement beaucoup de choses qui peuvent être réalisées sans avoir à plonger dans le code C/C++ de Blender.

L’exemple du tutoriel est limité, mais montre l’utilisation de l’API Blender dans des tâches courantes que vous pouvez étendre pour écrire vos propres outils.

Lectures recommandées

Blender est fourni avec des modèles commentés qui sont accessibles dans l’entête de l’éditeur de texte. Si vous avez des domaines spécifiques dont vous voulez voir le code exemple, c’est un bon point de départ.

Voici quelques sites que vous pourriez vouloir consulter après la lecture de ce manuel.