Tutorial: Acciones interactivas de script

Índice

 

Introducción

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

Eventos

Tan pronto como se inicia la acción de script, ésta maneja varios eventos hasta que se termina. Un evento es algo que ocurre si algo está sucediendo. Por ejemplo, si se inicia la acción de script, se llama a beginEvent. Si el usuario hace clic en una entidad se desencadena 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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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ú Misc > Ejemplos. El texto del menú es "EjemploMínimo".

Tenga en cuenta que para que se encuentre el script, el nombre del archivo tiene que coincidir con el nombre de la clase, es decir, "ExMiMinimo.js" en este caso. También necesita residir dentro de un directorio con el mismo nombre "ExMyMinimal", por lo que este script puede, por ejemplo, colocarse en scripts/Misc/ExMyMinimal/ExMyMinimal.js.

Añadir beginEvent

El script anterior es completamente funcional y puede ser disparado. Sin embargo, en realidad no hace nada cuando se dispara. Además, una vez disparado, el script permanece activo hasta que el usuario lo termina pulsando el botón derecho del ratón. Para cambiar esto, implementemos beginEvent para imprimir algo en el historial de la línea de comandos de QCAD y terminar la acción:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 ahora se inicia la herramienta Misc > Ejemplos > Ejemplo Mínimo, imprime "¡Hola Mundo!" en el historial de la línea de comandos (línea 12) y luego termina (línea 14).

Si una secuencia de comandos no requiere ninguna interacción del usuario, puede utilizarse para añadir un menú que haga algo y luego termine. Ejemplos de este tipo de acciones son Ver > Zoom automático, Seleccionar > Seleccionar todo, Editar > Borrar, etc.

Añadir interacción

En cuanto un script requiera algún tipo de interacción por parte del usuario, necesitamos implementar más manejadores de eventos y decirle al script lo que el usuario tiene que hacer 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 entra.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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 inmediatamente, sino que dejamos que se ejecute 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 actualizar la vista previa o 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 vista previa fijado en false para indicar que se ha elegido o introducido una coordenada definitiva.

updatePreview en la línea 22 previsualiza la operación devuelta por getOperation mientras que applyOperation en la línea 25 aplica realmente la operación a nuestro documento.

hay que implementargetOperation para que devuelva la operación a previsualizar o aplicar al documento. Esto es ligeramente más complejo que lo que hemos visto en la sencilla API anterior. Esto se debe a que una única operación puede utilizarse para añadir varios objetos, modificar objetos o eliminar objetos.

Añadir widgets a la barra de opciones

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 al usuario introducir un radio para el círculo. QCAD suele utilizar la barra de herramientas de opciones de la parte superior para mostrar y cambiar dichos parámetros de herramienta. Para ello, necesitamos definir qué widgets queremos mostrar en la barra de herramientas de opciones y qué parámetros controlan. Esto puede hacerse con un archivo UI, un archivo XML que define un widget y su contenido. Los archivos UI pueden diseñarse cómodamente utilizando un software llamado Qt Designer que viene como parte del conjunto de herramientas Qt. Para este ejemplo, utilizamos un archivo UI sencillo que también puede crearse en un editor de texto (archivo ExMyMinimal.ui):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?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 editor de línea (RMathLineEdit). Importante es el nombre de la edición de línea ("Radius"). El widget se vincula automáticamente a nuestro script a través de este nombre. Todo lo que tenemos que hacer en nuestro script es definir qué archivo de interfaz de usuario queremos utilizar (línea 9) e implementar un nuevo controlador de eventos llamado slotRadiusChanged, que es "slot" + [el nombre de nuestra edición de línea] + "Changed" (línea 45):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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 utiliza al crear el círculo en getOperation.

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

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