Salta ai contenuti

Azioni di script interattive

Le azioni di script sono script che aggiungono una voce a un menu e/o a una barra degli strumenti e che possono gestire le interazioni dell’utente. Le azioni di script rimangono attive finché non vengono terminate dall’utente o finché non si terminano da sole.

Non appena l’azione di script viene avviata, gestisce vari eventi finché non viene terminata. Un evento è qualcosa che si verifica quando accade qualcosa. Ad esempio, se l’azione di script viene avviata, viene chiamato beginEvent. Se l’utente fa clic su un’entità, viene attivato un evento pickEntity; se l’utente fa clic su una coordinata, si verifica un evento pickCoordinate, ecc.

La struttura minima di un’azione di script è la seguente:

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"]);
};

Questo script di esempio aggiunge un menu in fondo al menu Varie > Esempi. Il testo del menu è “Minimal Example”.

Si noti che, affinché lo script venga trovato, il nome del file deve corrispondere al nome della classe, ossia “ExMyMinimal.js” in questo caso. Deve inoltre risiedere all’interno di una cartella con lo stesso nome “ExMyMinimal”, in modo che questo script possa ad esempio essere collocato in scripts/Misc/ExMyMinimal/ExMyMinimal.js.

È inoltre possibile inserire i propri script in una cartella scripts locale all’interno della cartella home dell’utente. Per individuare la cartella esatta, aprire la finestra di dialogo «Informazioni» (Aiuto > Informazioni su QCAD…) e passare alla scheda Sistema. Lì è possibile vedere la posizione dei dati sotto Data directory. Questa è la directory sotto la quale occorre creare una sottocartella denominata scripts e, al suo interno, delle sottocartelle, una cartella per ogni strumento di script, per esempio /percorso/della/directory dei dati/scripts/MyScripts/MyScript1/MyScript1.js

La posizione esatta dipende dal sistema e dalla sua configurazione.

Lo script precedente è pienamente funzionante e può essere attivato. Tuttavia, non fa nulla quando viene attivato. Inoltre, una volta attivato, lo script rimane attivo finché l’utente non lo termina facendo clic con il tasto destro del mouse. Per cambiare questo comportamento, implementiamo beginEvent in modo che stampi qualcosa nella cronologia della riga di comando di QCAD e termini l’azione:

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"]);
};

Se lo strumento Varie > Esempi > Minimal Example viene ora avviato, stampa “Hello World!” nella cronologia della riga di comando (riga 12) e poi si termina (riga 14).

Se uno script non richiede alcuna interazione dell’utente, tale script può essere usato per aggiungere un menu che esegue qualcosa e poi si termina. Esempi di tali azioni sono Vista > Zoom automatico, Selezionare > Seleziona tutto, Modifica > Cancellare, ecc.

Non appena uno script richiede un qualsiasi tipo di interazione dell’utente, dobbiamo implementare altri gestori di eventi e indicare allo script cosa deve fare l’utente successivamente (ad esempio scegliere un’entità o definire una coordinata). Nel passo successivo, entriamo in uno stato in cui l’azione si aspetta una coordinata dall’utente. Disegniamo quindi un cerchio in ogni posizione in cui l’utente fa clic o che immette.

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"]);
};

Nel beginEvent non terminiamo più l’azione immediatamente, ma la lasciamo in esecuzione finché l’utente non la termina (clic destro o Esc). Implementiamo quindi pickCoordinate per memorizzare la posizione del cursore del mouse o la coordinata immessa e per aggiornare l’anteprima oppure applicare l’operazione (ossia aggiungere il cerchio). pickCoordinate viene chiamato ogni volta che l’utente muove il mouse per mostrare un’anteprima dell’operazione prevista. Quando l’utente fa clic o immette una coordinata, viene chiamato con il parametro preview impostato su false per indicare che è stata scelta o immessa una coordinata definitiva.

updatePreview alla riga 22 mostra un’anteprima dell’operazione restituita da getOperation, mentre applyOperation alla riga 25 applica effettivamente l’operazione al nostro documento.

getOperation deve essere implementato in modo da restituire l’operazione da visualizzare in anteprima o da applicare al documento. Questo è leggermente più complesso di quanto visto sopra con l’API semplice. Ciò è dovuto al fatto che una singola operazione può essere usata per aggiungere più oggetti, modificare oggetti o eliminare oggetti.

Il cerchio disegnato nel nostro esempio ha sempre un raggio di 1 unità di disegno (vedere riga 33). In un passo successivo, vogliamo consentire all’utente di immettere un raggio per il cerchio. QCAD di solito usa la barra delle opzioni in alto per visualizzare e modificare tali parametri dello strumento. A tal fine, dobbiamo definire quali widget vogliamo mostrare nella barra delle opzioni e quali parametri controllano. Ciò può essere fatto con un file UI, un file XML che definisce un widget e il suo contenuto. I file UI possono essere progettati comodamente con un software chiamato Qt Designer, che fa parte del toolkit di Qt. Per questo esempio, usiamo un semplice file UI che può anche essere creato in un editor di testo (file 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>

Il file UI definisce due widget: un’etichetta (QLabel) e un campo di immissione (RMathLineEdit). È importante il nome del campo di immissione (“Radius”). Il widget viene collegato automaticamente al nostro script tramite questo nome. Tutto ciò che dobbiamo fare nel nostro script è definire quale file UI vogliamo usare (riga 9) e implementare un nuovo gestore di eventi chiamato slotRadiusChanged, ossia “slot” + [il nome del nostro campo di immissione] + “Changed” (riga 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"]);
};

Questa nuova funzione slotRadiusChanged viene chiamata ogni volta che l’utente immette un nuovo raggio. Imposta la variabile membro this.radius, che a sua volta viene usata durante la creazione del cerchio in getOperation.

Tutti gli script di QCAD si basano su uno dei concetti descritti in questo tutorial.

Poiché ogni strumento in QCAD è implementato come uno script al livello superiore, sono disponibili numerosi script di esempio. È possibile trovarli nel nostro repository git.