Ir al contenido

Acciones de script interactivas

Las acciones de script son scripts que añaden una entrada a un menú y/o a una barra de herramientas y que pueden gestionar interacciones del usuario. Las acciones de script permanecen activas hasta que el usuario las termina o hasta que se autoterminan.

En cuanto se inicia la acción de script, gestiona diversos eventos hasta que finaliza. Un evento es algo que ocurre cuando sucede algo. Por ejemplo, si se inicia la acción de script, se llama a beginEvent. Si el usuario hace clic en una entidad, se dispara un evento pickEntity; si el usuario hace clic en una coordenada, se produce un evento pickCoordinate, etc.

La estructura mínima de una acción de script es la siguiente:

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

Este script de ejemplo añade un menú en la parte inferior del menú Misceláneo > Ejemplos. El texto del menú es “Minimal Example”.

Tenga en cuenta que, para que se encuentre el script, el nombre del archivo debe coincidir con el nombre de la clase, es decir, “ExMyMinimal.js” en este caso. También debe residir dentro de un directorio con el mismo nombre “ExMyMinimal”, de modo que este script pueda colocarse, por ejemplo, en scripts/Misc/ExMyMinimal/ExMyMinimal.js.

También puede colocar sus scripts en una carpeta de scripts local dentro de su carpeta de usuario. Para averiguar la carpeta exacta, abra el cuadro de diálogo «Acerca de» (Ayuda > Acerca de QCAD…) y vaya a la pestaña Sistema. Allí puede ver la ubicación de los datos en Data directory. Este es el directorio bajo el cual debe crear una subcarpeta llamada scripts y, dentro de ella, subcarpetas, una carpeta por cada herramienta de script, por ejemplo /ruta/al/directorio de datos/scripts/MyScripts/MyScript1/MyScript1.js

La ubicación exacta depende de su sistema y de su configuración.

El script anterior es totalmente funcional y se puede activar. Sin embargo, no hace nada cuando se activa. Además, una vez activado, el script permanece activo hasta que el usuario lo termina haciendo clic con el botón derecho del ratón. Para cambiar esto, implementemos beginEvent de modo que imprima algo en el historial de la línea de comandos de QCAD y termine la acción:

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 la herramienta Misceláneo > Ejemplos > Minimal Example se inicia ahora, imprime “Hello World!” en el historial de la línea de comandos (línea 12) y luego termina (línea 14).

Si un script no requiere ninguna interacción del usuario, se puede usar para añadir un menú que hace algo y luego termina. Ejemplos de tales acciones son Ver > Zoom automático, Seleccionar > Seleccionar todo, Editar > Borrar, etc.

En cuanto un script requiere algún tipo de interacción del usuario, necesitamos implementar más manejadores de eventos e indicar al script qué debe hacer el usuario a continuación (por ejemplo, elegir una entidad o definir una coordenada). En el siguiente paso, entramos en un estado en el que la acción espera una coordenada del usuario. A continuación, dibujamos un círculo en cada posición en la que el usuario hace clic o que introduce.

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

En el beginEvent, ya no terminamos la acción de inmediato, sino que la dejamos ejecutarse hasta que el usuario la termine (clic derecho o Escape). A continuación, implementamos pickCoordinate para almacenar la posición del cursor del ratón o la coordenada introducida y, o bien actualizar la vista previa, o bien aplicar la operación (es decir, añadir el círculo). pickCoordinate se llama cada vez que el usuario mueve el ratón para mostrar una vista previa de la operación prevista. Cuando el usuario hace clic o introduce una coordenada, se llama con el parámetro preview establecido en false para indicar que se ha elegido o introducido una coordenada definitiva.

updatePreview en la línea 22 muestra una vista previa de la operación devuelta por getOperation, mientras que applyOperation en la línea 25 aplica realmente la operación a nuestro documento.

getOperation debe implementarse para que devuelva la operación que se va a previsualizar o aplicar al documento. Esto es algo más complejo que lo que hemos visto arriba con la API simple. Esto se debe a que una sola operación se puede usar para añadir varios objetos, modificar objetos o eliminar objetos.

El círculo dibujado en nuestro ejemplo siempre tiene un radio de 1 unidad de dibujo (véase la línea 33). En un siguiente paso, queremos permitir que el usuario introduzca un radio para el círculo. QCAD suele usar la barra de opciones de la parte superior para mostrar y cambiar estos parámetros de herramienta. Para ello, necesitamos definir qué widgets queremos mostrar en la barra de opciones y qué parámetros controlan. Esto se puede hacer con un archivo UI, un archivo XML que define un widget y su contenido. Los archivos UI se pueden diseñar cómodamente con un software llamado Qt Designer, que forma parte del kit de herramientas de Qt. Para este ejemplo, usamos un archivo UI sencillo que también se puede crear en un editor de texto (archivo 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>

El archivo UI define dos widgets: una etiqueta (QLabel) y un campo de entrada (RMathLineEdit). Es importante el nombre del campo de entrada (“Radius”). El widget se vincula automáticamente a nuestro script mediante este nombre. Todo lo que tenemos que hacer en nuestro script es definir qué archivo UI queremos usar (línea 9) e implementar un nuevo manejador de eventos llamado slotRadiusChanged, es decir, “slot” + [el nombre de nuestro campo de entrada] + “Changed” (línea 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"]);
};

Esta nueva función slotRadiusChanged se llama cada vez que el usuario introduce un nuevo radio. Establece la variable miembro this.radius, que a su vez se usa al crear el círculo en getOperation.

Todos los scripts de QCAD se basan en uno de los conceptos descritos en este tutorial.

Dado que cada herramienta de QCAD se implementa como un script en el nivel superior, hay numerosos scripts de ejemplo disponibles. Puede encontrarlos en nuestro repositorio git.