Tutoriel sur les modules complémentaires#

Public visé#

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 (integer, 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 résoudre au mieux tout message d’erreur que Python affiche lors de l’écriture de scripts, vous exécutez Blender à partir d’un terminal. Voir Use The Terminal.

Astuce

Vous pouvez activer Developer Extras dans les préférences pour activer des fonctionnalités qui facilitent le développement de modules complémentaires.

Qu’est-ce qu’un module complémentaire ?#

Un module complémentaire 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 le module complémentaire 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 du module complémentaire telles que le titre, la version et l’auteur à afficher dans la liste des modules complémentaires des Preferences. Il spécifie également la version minimale de Blender requise pour lancer le script ; les versions plus anciennes ne vont pas afficher l’add-on dans la liste.

register

est une fonction qui se lance seulement une fois le module complémentaire activé, ce qui signifie que le module peut être chargé sans activation du module complémentaire.

unregister

est une fonction pour décharger tout ce qui a été installé par register, celle-ci est appelée quand le module complémentaire est désactivé.

Notez que ce module complémentaire ne fait rien concernant Blender (le module blender_api:bpy n’est pas importé par exemple).

Ceci est un exemple artificiel d’un module complémentaire qui sert à illustrer le fait que les exigences de base d’un module complémentaire sont simples.

Un module complémentaire 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 module complémentaire qui s’autorise à s’intégrer dans Blender, une telle fonctionnalité est simplement offerte par le module blender_api:bpy pour que n’importe quel script puisse y accéder.

Aussi un module complémentaire 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 module complémentaire#

Le module complémentaire le plus simple possible ci-dessus est utile comme exemple mais pas plus. Le module complémentaire 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 unité.

Écrire le Le module complémentaire (simple)#

Ce Le module complémentaire 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 menu_func(self, context):
    self.layout.operator(ObjectMoveX.bl_idname)

def register():
    bpy.utils.register_class(ObjectMoveX)
    bpy.types.VIEW3D_MT_object.append(menu_func)  # Adds the new operator to an existing menu.

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.#

Ouvrez 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 ce module complémentaire ouvert dans Blender pour la prochaine étape - installation.

Installer le module complémentaire#

Une fois que vous avez votre module complémentaire 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 le module complémentaire 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 module complémentaire, 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 module complémentaire téléchargé.

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

Maintenant le module complémentaire sera listé et vous pouvez l’activer en appuyant sur la case à cocher, si vous voulez qu’il soit activé au redémarrage, appuyez sur Save as Default. L’opérateur peut être exécuté de la même manière que celle décrite dans la section précédente.

Quand le module complémentaire est activé, Blender exécute le code et lance la fonction register(). Quand le module complémentaire est désactivé, Blender lance la fonction unregister().

Note

La destination d’un module complémentaire dépend de votre configuration de Blender. Pendant l’installation d’un module complémentaire les chemins de la source et de la destination sont affichés sur la console. Vous pouvez aussi trouver les emplacements des chemins du module complémentaire 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 module complémentaire#

Pour notre second module complémentaire, 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 module complémentaire :

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 module complémentaire, 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 le module complémentaire#

La première étape est de convertir le script tel quel en un module complémentaire :

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 le module complémentaire et considérer ce qui pourrait être fait pour le rendre plus utile.

The two of the most obvious missing things are – having the total fixed at 10, and having to access the operator with Search is not very convenient.

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

Propriété d’opérateur#

Il existe une variété de types de propriétés qui sont utilisées pour les paramètres des outils, les types de propriétés communes comprennent : int, float, vecteur, couleur, booléen et chaîne.

Ces propriétés sont gérées différemment des attributs de classe Python typiques parce que Blender a besoin de les afficher dans l’interface, d’enregistrer leurs réglages dans les configurations de clavier et de conserver les réglages pour une réutilisation.

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 débarrasser du littéral 10 pour total, nous utiliserons la propriété d’un opérateur. Les propriétés de l’opérateur sont définies via le module bpy.props, celui-ci 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.

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é .

Disposition du clavier (Keymap)#

Dans Blender, les modules complémentaires ont leur propre configuration de clavier de façon à ne pas interférer avec les configurations de clavier intégrées de Blender.

Dans l’exemple ci-dessous, un nouveau mode objet bpy.types.KeyMap est ajouté, puis un bpy.types.KeyMapItem est ajouté à la configuration de clavier qui fait référence à notre opérateur nouvellement ajouté, en utilisant Maj-Ctrl-T comme raccourci clavier pour l’activer.

# 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

Bien que Maj-Ctrl-T ne soit pas un raccourci clavier par défaut de Blender, il est difficile de s’assurer que les modules complémentaires ne s’écraseront pas n’écraseront pas les claviers des autres. Ainsi veillez, au moins, à ne pas entrer en conflit avec des fonctionnalités importantes de Blender (voir aussi is key free add-on).

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 modules complémentaires ne vont pas s’enregistrer plusieurs fois une fois activé via les Preferences.

Conclusions#

Les modules complémentaires 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.

Références complémentaires#

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.