Aller au contenu

Actions de script interactives

Les actions de script sont des scripts qui ajoutent une entrée à un menu et/ou à une barre d’outils et qui peuvent gérer les interactions de l’utilisateur. Les actions de script restent actives jusqu’à ce qu’elles soient terminées par l’utilisateur ou jusqu’à ce qu’elles se terminent d’elles-mêmes.

Dès que l’action de script est démarrée, elle gère divers événements jusqu’à ce qu’elle soit terminée. Un événement est quelque chose qui se produit lorsqu’il se passe quelque chose. Par exemple, si l’action de script est démarrée, beginEvent est appelé. Si l’utilisateur clique sur une entité, un événement pickEntity est déclenché ; si l’utilisateur clique sur une coordonnée, un événement pickCoordinate se produit, etc.

La structure minimale d’une action de script est la suivante :

include("scripts/EAction.js");
function ExMyMinimal(guiAction) {
EAction.call(this, guiAction);
}
ExMyMinimal.prototype = new EAction();
ExMyMinimal.init = function(basePath) {
var action = new RGuiAction(qsTr("&Minimal Example"), RMainWindowQt.getMainWindow());
action.setRequiresDocument(true);
action.setScriptFile(basePath + "/ExMyMinimal.js");
action.setGroupSortOrder(100000);
action.setSortOrder(0);
action.setWidgetNames(["ExamplesMenu"]);
};

Ce script d’exemple ajoute un menu en bas du menu Divers > Exemples. Le texte du menu est “Minimal Example”.

Notez que pour que le script soit trouvé, le nom du fichier doit correspondre au nom de la classe, c’est-à-dire “ExMyMinimal.js” dans ce cas. Il doit également se trouver dans un répertoire du même nom “ExMyMinimal”, de sorte que ce script peut par exemple être placé dans scripts/Misc/ExMyMinimal/ExMyMinimal.js.

Vous pouvez également placer vos scripts dans un dossier scripts local situé dans votre dossier personnel. Pour connaître le dossier exact, ouvrez la boîte de dialogue « À propos » (Aide > À propos de QCAD…) et rendez-vous dans l’onglet Système. L’emplacement des données y est indiqué sous Data directory. C’est le répertoire dans lequel vous devez créer un sous-dossier nommé scripts, puis des sous-dossiers, un dossier par outil de script, par exemple /chemin/vers/le répertoire de données/scripts/MyScripts/MyScript1/MyScript1.js

L’emplacement exact dépend de votre système et de sa configuration.

Le script ci-dessus est pleinement fonctionnel et peut être déclenché. Cependant, il ne fait rien lorsqu’il est déclenché. De plus, une fois déclenché, le script reste actif jusqu’à ce que l’utilisateur le termine en cliquant avec le bouton droit de la souris. Pour changer cela, implémentons beginEvent afin d’afficher quelque chose dans l’historique de la ligne de commande de QCAD et de terminer l’action :

include("scripts/EAction.js");
function ExMyMinimal(guiAction) {
EAction.call(this, guiAction);
}
ExMyMinimal.prototype = new EAction();
ExMyMinimal.prototype.beginEvent = function() {
EAction.prototype.beginEvent.call(this);
EAction.handleUserMessage("Hello World!");
this.terminate();
};
ExMyMinimal.init = function(basePath) {
var action = new RGuiAction(qsTr("&Minimal Example"), RMainWindowQt.getMainWindow());
action.setRequiresDocument(true);
action.setScriptFile(basePath + "/ExMyMinimal.js");
action.setGroupSortOrder(100000);
action.setSortOrder(0);
action.setWidgetNames(["ExamplesMenu"]);
};

Si l’outil Divers > Exemples > Minimal Example est maintenant démarré, il affiche “Hello World!” dans l’historique de la ligne de commande (ligne 12) puis se termine (ligne 14).

Si un script ne nécessite aucune interaction de l’utilisateur, un tel script peut être utilisé pour ajouter un menu qui fait quelque chose puis se termine. Des exemples de telles actions sont Affichage > Zoom automatique, Sélection > Sélectionner tout, Édition > Supprimer, etc.

Dès qu’un script nécessite une quelconque interaction de l’utilisateur, nous devons implémenter davantage de gestionnaires d’événements et indiquer au script ce que l’utilisateur doit faire ensuite (par exemple choisir une entité ou définir une coordonnée). À l’étape suivante, nous entrons dans un état où l’action attend une coordonnée de la part de l’utilisateur. Nous dessinons ensuite un cercle à chaque position que l’utilisateur clique ou saisit.

include("scripts/EAction.js");
function ExMyMinimal(guiAction) {
EAction.call(this, guiAction);
this.pos = undefined;
}
ExMyMinimal.prototype = new EAction();
ExMyMinimal.prototype.beginEvent = function() {
EAction.prototype.beginEvent.call(this);
var di = this.getDocumentInterface();
di.setClickMode(RAction.PickCoordinate);
};
ExMyMinimal.prototype.pickCoordinate = function(event, preview) {
this.pos = event.getModelPosition();
if (preview) {
this.updatePreview();
}
else {
this.applyOperation();
}
};
ExMyMinimal.prototype.getOperation = function(preview) {
var doc = this.getDocument();
var op = new RAddObjectOperation();
var circle = new RCircle(this.pos, 1);
op.addObject(shapeToEntity(doc, circle));
return op;
};
ExMyMinimal.init = function(basePath) {
var action = new RGuiAction(qsTr("&Minimal Example"), RMainWindowQt.getMainWindow());
action.setRequiresDocument(true);
action.setScriptFile(basePath + "/ExMyMinimal.js");
action.setGroupSortOrder(100000);
action.setSortOrder(0);
action.setWidgetNames(["ExamplesMenu"]);
};

Dans le beginEvent, nous ne terminons plus l’action immédiatement, mais la laissons s’exécuter jusqu’à ce que l’utilisateur la termine (clic droit ou Échap). Nous implémentons ensuite pickCoordinate pour mémoriser la position du curseur de la souris ou la coordonnée saisie et pour, soit mettre à jour l’aperçu, soit appliquer l’opération (c’est-à-dire ajouter le cercle). pickCoordinate est appelé chaque fois que l’utilisateur déplace la souris afin d’afficher un aperçu de l’opération prévue. Lorsque l’utilisateur clique ou saisit une coordonnée, il est appelé avec le paramètre preview réglé sur false pour indiquer qu’une coordonnée définitive a été choisie ou saisie.

updatePreview à la ligne 22 affiche un aperçu de l’opération renvoyée par getOperation, tandis que applyOperation à la ligne 25 applique effectivement l’opération à notre document.

getOperation doit être implémenté pour renvoyer l’opération à prévisualiser ou à appliquer au document. C’est légèrement plus complexe que ce que nous avons vu ci-dessus avec l’API simple. Cela s’explique par le fait qu’une seule opération peut être utilisée pour ajouter plusieurs objets, modifier des objets ou supprimer des objets.

Le cercle dessiné dans notre exemple a toujours un rayon de 1 unité de dessin (voir ligne 33). À l’étape suivante, nous voulons permettre à l’utilisateur de saisir un rayon pour le cercle. QCAD utilise généralement la barre d’options en haut pour afficher et modifier de tels paramètres d’outil. Pour cela, nous devons définir quels widgets nous voulons afficher dans la barre d’options et quels paramètres ils contrôlent. Cela peut se faire avec un fichier UI, un fichier XML qui définit un widget et son contenu. Les fichiers UI peuvent être conçus confortablement à l’aide d’un logiciel appelé Qt Designer, qui fait partie de la boîte à outils Qt. Pour cet exemple, nous utilisons un simple fichier UI qui peut aussi être créé dans un éditeur de texte (fichier ExMyMinimal.ui) :

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExMyMinimal</class>
<widget class="QWidget" name="ExMyMinimal">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="RadiusLabel">
<property name="text">
<string>&amp;Radius:</string>
</property>
<property name="buddy">
<cstring>Radius</cstring>
</property>
</widget>
</item>
<item>
<widget class="RMathLineEdit" name="Radius">
<property name="text">
<string notr="true">1</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RMathLineEdit</class>
<extends>QLineEdit</extends>
<header>RMathLineEdit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

Le fichier UI définit deux widgets : une étiquette (QLabel) et un champ de saisie (RMathLineEdit). Le nom du champ de saisie (“Radius”) est important. Le widget est automatiquement lié à notre script grâce à ce nom. Tout ce que nous avons à faire dans notre script est de définir quel fichier UI nous voulons utiliser (ligne 9) et d’implémenter un nouveau gestionnaire d’événements appelé slotRadiusChanged, c’est-à-dire “slot” + [le nom de notre champ de saisie] + “Changed” (ligne 45) :

include("scripts/EAction.js");
function ExMyMinimal(guiAction) {
EAction.call(this, guiAction);
this.pos = undefined;
this.radius = undefined;
this.setUiOptions("ExMyMinimal.ui");
}
ExMyMinimal.prototype = new EAction();
ExMyMinimal.prototype.beginEvent = function() {
EAction.prototype.beginEvent.call(this);
var di = this.getDocumentInterface();
di.setClickMode(RAction.PickCoordinate);
};
ExMyMinimal.prototype.pickCoordinate = function(event, preview) {
this.pos = event.getModelPosition();
if (preview) {
this.updatePreview();
}
else {
this.applyOperation();
}
};
ExMyMinimal.prototype.getOperation = function(preview) {
if (isNull(this.pos) || isNull(this.radius)) {
return undefined;
}
var doc = this.getDocument();
var op = new RAddObjectOperation();
var circle = new RCircle(this.pos, this.radius);
op.addObject(shapeToEntity(doc, circle));
return op;
};
ExMyMinimal.prototype.slotRadiusChanged = function(v) {
this.radius = v;
this.updatePreview();
};
ExMyMinimal.init = function(basePath) {
var action = new RGuiAction(qsTr("&Minimal Example"), RMainWindowQt.getMainWindow());
action.setRequiresDocument(true);
action.setScriptFile(basePath + "/ExMyMinimal.js");
action.setGroupSortOrder(100000);
action.setSortOrder(0);
action.setWidgetNames(["ExamplesMenu"]);
};

Cette nouvelle fonction slotRadiusChanged est appelée chaque fois que l’utilisateur saisit un nouveau rayon. Elle définit la variable membre this.radius, qui est à son tour utilisée lors de la création du cercle dans getOperation.

Tous les scripts de QCAD reposent sur l’un des concepts présentés dans ce tutoriel.

Comme chaque outil de QCAD est implémenté au plus haut niveau sous forme de script, de nombreux scripts d’exemple sont disponibles. Vous les trouverez dans notre dépôt git.