Pular para o conteúdo

Ações de script interativas

Ações de script são scripts que adicionam uma entrada a um menu e/ou barra de ferramentas e que podem tratar interações do usuário. As ações de script permanecem ativas até serem encerradas pelo usuário ou até se autoencerrarem.

Assim que a ação de script é iniciada, ela trata diversos eventos até ser encerrada. Um evento é algo que ocorre quando algo acontece. Por exemplo, se a ação de script for iniciada, beginEvent é chamado. Se o usuário clicar em uma entidade, um evento pickEntity é disparado; se o usuário clicar em uma coordenada, ocorre um evento pickCoordinate, etc.

A estrutura mínima de uma ação de script é a seguinte:

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 exemplo adiciona um menu na parte inferior do menu Miscelânea > Exemplos. O texto do menu é “Minimal Example”.

Observe que, para que o script seja encontrado, o nome do arquivo precisa corresponder ao nome da classe, ou seja, “ExMyMinimal.js” neste caso. Ele também precisa residir dentro de um diretório com o mesmo nome “ExMyMinimal”, de modo que este script possa, por exemplo, ser colocado em scripts/Misc/ExMyMinimal/ExMyMinimal.js.

Você também pode colocar os seus scripts em uma pasta scripts local dentro da sua pasta de usuário. Para descobrir a pasta exata, abra a caixa de diálogo «Sobre» (Ajuda > Sobre o QCAD…) e vá até a aba Sistema. Ali você pode ver a localização dos dados em Data directory. Esse é o diretório sob o qual você deve criar uma subpasta chamada scripts e, dentro dela, subpastas, uma pasta para cada ferramenta de script, por exemplo /caminho/para/o diretório de dados/scripts/MyScripts/MyScript1/MyScript1.js

A localização exata depende do seu sistema e da sua configuração.

O script acima é totalmente funcional e pode ser acionado. No entanto, ele não faz nada quando acionado. Além disso, uma vez acionado, o script permanece ativo até que o usuário o encerre clicando com o botão direito do mouse. Para mudar isso, vamos implementar beginEvent de modo que ele imprima algo no histórico da linha de comando do QCAD e encerre a ação:

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 a ferramenta Miscelânea > Exemplos > Minimal Example for iniciada agora, ela imprime “Hello World!” no histórico da linha de comando (linha 12) e depois se encerra (linha 14).

Se um script não exigir nenhuma interação do usuário, esse tipo de script pode ser usado para adicionar um menu que faz algo e depois se encerra. Exemplos de tais ações são Ver > Zoom automático, Selecione > Seleccionar tudo, Editar > Eliminar, etc.

Assim que um script exige qualquer tipo de interação do usuário, precisamos implementar mais tratadores de eventos e informar ao script o que o usuário deve fazer em seguida (por exemplo, escolher uma entidade ou definir uma coordenada). No próximo passo, entramos em um estado no qual a ação espera uma coordenada do usuário. Em seguida, desenhamos um círculo em cada posição em que o usuário clica ou que insere.

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

No beginEvent, não encerramos mais a ação imediatamente, mas a deixamos em execução até que o usuário a encerre (clique com o botão direito ou Esc). Em seguida, implementamos pickCoordinate para armazenar a posição do cursor do mouse ou a coordenada inserida e para atualizar a prévia ou aplicar a operação (ou seja, adicionar o círculo). pickCoordinate é chamado sempre que o usuário move o mouse para exibir uma prévia da operação planejada. Quando o usuário clica ou insere uma coordenada, ele é chamado com o parâmetro preview definido como false para indicar que uma coordenada definitiva foi escolhida ou inserida.

updatePreview na linha 22 exibe uma prévia da operação retornada por getOperation, enquanto applyOperation na linha 25 aplica de fato a operação ao nosso documento.

getOperation deve ser implementado para retornar a operação a ser pré-visualizada ou aplicada ao documento. Isso é um pouco mais complexo do que vimos acima com a simple API. Isso ocorre porque uma única operação pode ser usada para adicionar vários objetos, modificar objetos ou excluir objetos.

O círculo desenhado no nosso exemplo tem sempre um raio de 1 unidade de desenho (veja a linha 33). Em um próximo passo, queremos permitir que o usuário insira um raio para o círculo. O QCAD normalmente usa a barra de opções na parte superior para exibir e alterar esses parâmetros de ferramenta. Para isso, precisamos definir quais widgets queremos mostrar na barra de opções e quais parâmetros eles controlam. Isso pode ser feito com um arquivo UI, um arquivo XML que define um widget e o seu conteúdo. Os arquivos UI podem ser projetados confortavelmente com um software chamado Qt Designer, que faz parte do kit de ferramentas Qt. Para este exemplo, usamos um arquivo UI simples que também pode ser criado em um editor de texto (arquivo 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>

O arquivo UI define dois widgets: um rótulo (QLabel) e um campo de entrada (RMathLineEdit). O nome do campo de entrada (“Radius”) é importante. O widget é vinculado automaticamente ao nosso script por meio desse nome. Tudo o que precisamos fazer no nosso script é definir qual arquivo UI queremos usar (linha 9) e implementar um novo tratador de eventos chamado slotRadiusChanged, ou seja, “slot” + [o nome do nosso campo de entrada] + “Changed” (linha 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 nova função slotRadiusChanged é chamada sempre que o usuário insere um novo raio. Ela define a variável membro this.radius, que por sua vez é usada ao criar o círculo em getOperation.

Todos os scripts do QCAD baseiam-se em um dos conceitos descritos neste tutorial.

Como cada ferramenta do QCAD é implementada como um script no nível superior, há muitos scripts de exemplo disponíveis. Você pode encontrá-los no nosso repositório git.