Les reactors
Dans le chapitre précédent, nous avons vu comment lier des entités entre elles par leurs handles. Cela ouvre une question naturelle : peut-on réagir automatiquement quand une entité liée est modifiée ou supprimée ? C'est exactement le rôle des reactors.
Un reactor est un mécanisme qui surveille des événements dans AutoCAD et exécute une fonction de rappel (callback) quand l'événement se produit. Si vous connaissez les événements en C# (par exemple button.Click += OnClick) ou les event listeners en JavaScript, c'est le même concept : on s'abonne à un événement et on fournit une fonction qui sera appelée quand il se produit.
Types de reactors
AutoLISP propose plusieurs catégories de reactors via les fonctions vlr-* (Visual LISP Reactors) :
| Type | Fonction de création | Surveille... |
|---|---|---|
| Objet | vlr-object-reactor |
Les modifications d'entités spécifiques |
| Base de données | vlr-dwg-reactor |
L'ouverture, la fermeture, la sauvegarde du dessin |
| Éditeur | vlr-editor-reactor |
Les commandes exécutées par l'utilisateur |
| Commande | vlr-command-reactor |
Le début et la fin de commandes spécifiques |
Reactor d'objet : surveiller une entité
Le reactor d'objet est le plus courant. Il surveille une ou plusieurs entités et déclenche une fonction quand elles sont modifiées :
(defun notifier-modification (proprietaire reactor liste-parametres)
(princ (strcat "\nL'entité " (vl-princ-to-string proprietaire) " a été modifiée !"))
(princ)
)
;; Créer un cercle
(entmake
(list
'(0 . "CIRCLE")
'(10 50.0 50.0 0.0)
'(40 . 25.0)
)
)
;; Attacher un reactor à ce cercle
(setq mon-reactor
(vlr-object-reactor
(list (vlax-ename->vla-object (entlast))) ; Liste d'entités à surveiller
"Surveillance cercle" ; Description (données utilisateur)
'((:vlr-modified . notifier-modification)) ; Liste d'événements et callbacks
)
)
Désormais, chaque fois que le cercle est déplacé, redimensionné ou modifié de quelque façon que ce soit, le message s'affiche.
La signature des callbacks
Chaque fonction de rappel reçoit trois arguments :
proprietaire— l'objet VLA (pas un ename) de l'entité qui a déclenché l'événementreactor— le reactor lui-même, utile pour accéder aux données utilisateur ou se supprimerliste-parametres— une liste d'informations supplémentaires sur l'événement (souventnil)
Pour convertir l'objet VLA en ename (nécessaire pour entget), utilisez vlax-vla-object->ename :
(defun mon-callback (proprietaire reactor liste-parametres / entite donnees)
(setq entite (vlax-vla-object->ename proprietaire))
(setq donnees (entget entite))
;; Traiter les données...
(princ)
)
Attention : la fonction de rappel d'un reactor d'objet ne doit jamais modifier l'entité surveillée directement dans le callback
:vlr-modified. Cela provoquerait une boucle infinie (la modification déclenche le reactor, qui modifie l'entité, ce qui déclenche le reactor...). Si vous devez modifier une entité en réponse à un événement, utilisez le callback:vlr-objectClosed.
Événements disponibles pour les reactors d'objet
| Événement | Se déclenche quand... |
|---|---|
:vlr-modified |
L'entité est modifiée |
:vlr-erased |
L'entité est supprimée |
:vlr-copied |
L'entité est copiée |
:vlr-objectClosed |
L'entité est fermée après modification (moment sûr pour modifier) |
La distinction entre :vlr-modified et :vlr-objectClosed est importante. Le premier se déclenche pendant la modification (l'entité est encore « ouverte » en écriture), tandis que le second se déclenche après (l'entité est « fermée » et ses données sont à jour). C'est dans :vlr-objectClosed que vous pouvez lire les nouvelles valeurs et modifier d'autres entités en toute sécurité.
Reactor de base de données : surveiller le dessin
Les reactors de base de données surveillent les événements au niveau du fichier DWG :
(defun avant-sauvegarde (reactor arguments)
(princ "\nSauvegarde en cours — vérification des données...")
;; Ici, vous pourriez vérifier l'intégrité des liens entre entités
(princ)
)
(defun apres-sauvegarde (reactor arguments)
(princ "\nSauvegarde terminée.")
(princ)
)
(vlr-dwg-reactor
nil
'((:vlr-beginSave . avant-sauvegarde)
(:vlr-saveComplete . apres-sauvegarde))
)
Événements disponibles pour les reactors de dessin
| Événement | Se déclenche quand... |
|---|---|
:vlr-beginSave |
Le dessin va être sauvegardé |
:vlr-saveComplete |
La sauvegarde est terminée |
:vlr-beginClose |
Le dessin va être fermé |
:vlr-databaseToBeDestroyed |
La base de données va être détruite |
Reactor de commande : réagir aux commandes
Les reactors de commande se déclenchent quand l'utilisateur (ou un programme) exécute une commande AutoCAD :
(defun surveiller-effacement (reactor arguments)
(if (= (car arguments) "ERASE")
(princ "\nAttention : des entités vont être supprimées !")
)
(princ)
)
(vlr-command-reactor
nil
'((:vlr-commandWillStart . surveiller-effacement))
)
Événements disponibles pour les reactors de commande
| Événement | Se déclenche quand... |
|---|---|
:vlr-commandWillStart |
Une commande est sur le point de démarrer |
:vlr-commandEnded |
Une commande vient de se terminer |
:vlr-commandCancelled |
Une commande a été annulée (Échap) |
:vlr-commandFailed |
Une commande a échoué |
Le premier argument de la liste arguments contient le nom de la commande en majuscules (par exemple "ERASE", "MOVE", "COPY").
Gérer les reactors
Supprimer un reactor
Les reactors consomment des ressources et peuvent ralentir AutoCAD si vous en créez trop. Pensez à les supprimer quand ils ne sont plus nécessaires :
;; Supprimer un reactor spécifique
(vlr-remove mon-reactor)
Lister les reactors actifs
;; Lister tous les reactors actifs, groupés par type
(vlr-reactors)
Accéder aux données utilisateur
Le deuxième argument de vlr-object-reactor (la chaîne "Surveillance cercle" dans notre exemple) est un champ libre. Vous pouvez y stocker n'importe quelle valeur AutoLISP — un handle, une liste, un nom de configuration. Pour le relire :
(vlr-data mon-reactor) ; → "Surveillance cercle"
C'est très utile pour transmettre du contexte à vos callbacks sans utiliser de variables globales.
Exemple pratique : maintenir un lien entre entités
Combinons les handles (vus au chapitre précédent) et les reactors pour maintenir un lien entre un cercle et un texte qui affiche son rayon. Quand l'utilisateur modifie le cercle, le texte se met à jour automatiquement :
(defun mettre-a-jour-texte (proprietaire reactor liste-parametres /
entite-cercle donnees-cercle rayon
handle-texte entite-texte donnees-texte)
;; Récupérer le rayon du cercle (après fermeture, donc les données sont à jour)
(setq entite-cercle (vlax-vla-object->ename proprietaire))
(setq donnees-cercle (entget entite-cercle))
(setq rayon (cdr (assoc 40 donnees-cercle)))
;; Retrouver le texte lié via le handle dans les xdata
(setq donnees-cercle (entget entite-cercle '("WIIP_LIEN")))
(setq handle-texte (cdr (assoc 1005 (cdadr (assoc -3 donnees-cercle)))))
(if handle-texte
(progn
(setq entite-texte (handent handle-texte))
(if entite-texte
(progn
;; Mettre à jour le contenu du texte
(setq donnees-texte (entget entite-texte))
(setq donnees-texte
(subst
(cons 1 (strcat "R=" (rtos rayon 2 2)))
(assoc 1 donnees-texte)
donnees-texte
)
)
(entmod donnees-texte)
(entupd entite-texte)
)
)
)
)
(princ)
)
(defun c:creer-cercle-avec-etiquette (/ centre rayon entite-cercle donnees-cercle
handle-cercle entite-texte donnees-texte
handle-texte)
(regapp "WIIP_LIEN")
;; Créer le cercle
(setq centre (getpoint "\nCentre du cercle : "))
(setq rayon (getreal "\nRayon : "))
(entmake
(list
'(0 . "CIRCLE")
(cons 10 (append centre '(0.0)))
(cons 40 rayon)
)
)
(setq entite-cercle (entlast))
(setq handle-cercle (cdr (assoc 5 (entget entite-cercle))))
;; Créer le texte d'étiquette
(entmake
(list
'(0 . "TEXT")
(cons 10 (list (+ (car centre) rayon 5.0) (cadr centre) 0.0))
(cons 40 (* rayon 0.3))
(cons 1 (strcat "R=" (rtos rayon 2 2)))
)
)
(setq entite-texte (entlast))
(setq handle-texte (cdr (assoc 5 (entget entite-texte))))
;; Lier le cercle au texte via xdata
(setq donnees-cercle (entget entite-cercle))
(setq donnees-cercle
(append donnees-cercle
(list
(list -3
(list "WIIP_LIEN"
(cons 1005 handle-texte)
)
)
)
)
)
(entmod donnees-cercle)
;; Attacher un reactor au cercle
(vlr-object-reactor
(list (vlax-ename->vla-object entite-cercle))
"Mise à jour étiquette rayon"
'((:vlr-objectClosed . mettre-a-jour-texte))
)
(princ "\nCercle créé avec étiquette liée.")
(princ)
)
Quand l'utilisateur modifie le rayon du cercle (par étirement, mise à l'échelle, etc.), le texte se met à jour automatiquement. Notez l'utilisation de :vlr-objectClosed et non :vlr-modified — c'est le moment sûr pour lire les nouvelles données du cercle et modifier le texte.
Résumé
| Concept | Fonction / Syntaxe | Description |
|---|---|---|
| Reactor d'objet | vlr-object-reactor |
Surveille les modifications d'entités spécifiques |
| Reactor de dessin | vlr-dwg-reactor |
Surveille les événements du fichier DWG |
| Reactor de commande | vlr-command-reactor |
Surveille les commandes exécutées |
| Supprimer un reactor | vlr-remove |
Libère les ressources du reactor |
| Lister les reactors | vlr-reactors |
Retourne tous les reactors actifs |
| Données utilisateur | vlr-data |
Lit le champ libre du reactor |
| VLA → ename | vlax-vla-object->ename |
Convertit un objet VLA en ename pour entget |
| Événement clé | Quand l'utiliser |
|---|---|
:vlr-modified |
Pour être notifié d'un changement (lecture seule) |
:vlr-objectClosed |
Pour modifier d'autres entités en réponse au changement |
:vlr-erased |
Pour nettoyer les liens quand une entité est supprimée |
:vlr-commandWillStart |
Pour intercepter une commande avant qu'elle ne s'exécute |
Dans le prochain chapitre, nous verrons comment communiquer avec des applications externes comme Microsoft Excel grâce à l'interopérabilité ActiveX.
Besoin d'un développement AutoCAD (AutoLISP, ObjectARX, .NET, VBA) ? Contactez-moi pour un devis gratuit.