Interactieve scriptacties
Inleiding
Section titled “Inleiding”Scriptacties zijn scripts die een item aan een menu en/of werkbalk toevoegen en die gebruikersinteracties kunnen afhandelen. Scriptacties blijven actief totdat ze door de gebruiker worden beëindigd of totdat ze zichzelf beëindigen.
Gebeurtenissen
Section titled “Gebeurtenissen”Zodra de scriptactie is gestart, handelt ze diverse gebeurtenissen af totdat ze wordt beëindigd. Een gebeurtenis is iets wat optreedt wanneer er iets gebeurt. Als de scriptactie bijvoorbeeld wordt gestart, wordt beginEvent aangeroepen. Als de gebruiker op een object klikt, wordt een pickEntity-gebeurtenis geactiveerd; als de gebruiker op een coördinaat klikt, treedt een pickCoordinate-gebeurtenis op, enzovoort.
De minimale structuur van een scriptactie is als volgt:
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"]);};Dit voorbeeldscript voegt onderaan het menu Diverse > Voorbeelden een menu toe. De menutekst luidt “Minimal Example”.
Let op: om het script te kunnen vinden, moet de bestandsnaam overeenkomen met de klassenaam, dat wil zeggen “ExMyMinimal.js” in dit geval. Het moet zich bovendien bevinden in een map met dezelfde naam “ExMyMinimal”, zodat dit script bijvoorbeeld in scripts/Misc/ExMyMinimal/ExMyMinimal.js kan worden geplaatst.
U kunt uw scripts ook in een lokale scripts-map binnen uw persoonlijke map plaatsen. Om de exacte map te achterhalen, opent u het Over-dialoogvenster (Hulp > Over QCAD…) en gaat u naar het tabblad Systeem. Daar ziet u de gegevenslocatie onder Data directory. Dit is de map waarin u een submap met de naam scripts moet aanmaken, en daarin submappen, één map per scriptgereedschap, bijvoorbeeld /pad/naar/gegevensmap/scripts/MyScripts/MyScript1/MyScript1.js
De exacte locatie hangt af van uw systeem en de configuratie ervan.
beginEvent toevoegen
Section titled “beginEvent toevoegen”Het bovenstaande script is volledig functioneel en kan worden geactiveerd. Het doet echter niets wanneer het wordt geactiveerd. Bovendien blijft het script na activering actief totdat de gebruiker het beëindigt door met de rechtermuisknop te klikken. Om dit te wijzigen, implementeren we beginEvent zodanig dat het iets naar de opdrachtregelgeschiedenis van QCAD schrijft en de actie beëindigt:
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"]);};Als het gereedschap Diverse > Voorbeelden > Minimal Example nu wordt gestart, schrijft het “Hello World!” naar de opdrachtregelgeschiedenis (regel 12) en beëindigt het zich vervolgens (regel 14).
Als een script geen enkele gebruikersinteractie vereist, kan zo’n script worden gebruikt om een menu toe te voegen dat iets doet en zich dan beëindigt. Voorbeelden van dergelijke acties zijn Aanzicht > Auto zoom, Selecteer > Selecteer alles, Bewerken > Verwijderen, enzovoort.
Interactie toevoegen
Section titled “Interactie toevoegen”Zodra een script enige vorm van gebruikersinteractie vereist, moeten we meer gebeurtenishandlers implementeren en het script vertellen wat de gebruiker vervolgens moet doen (bijvoorbeeld een object kiezen of een coördinaat definiëren). In de volgende stap gaan we een toestand binnen waarin de actie een coördinaat van de gebruiker verwacht. Vervolgens tekenen we op elke positie waar de gebruiker klikt of die hij invoert een cirkel.
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"]);};In het beginEvent beëindigen we de actie niet langer meteen, maar laten we haar draaien totdat de gebruiker haar beëindigt (rechtermuisklik of Escape). Vervolgens implementeren we pickCoordinate om de positie van de muiscursor of de ingevoerde coördinaat op te slaan en om ofwel het voorbeeld bij te werken, ofwel de bewerking toe te passen (dat wil zeggen de cirkel toe te voegen). pickCoordinate wordt aangeroepen telkens wanneer de gebruiker de muis beweegt om een voorbeeld van de geplande bewerking te tonen. Wanneer de gebruiker klikt of een coördinaat invoert, wordt het aangeroepen met de parameter preview op false om aan te geven dat een definitieve coördinaat is gekozen of ingevoerd.
updatePreview op regel 22 toont een voorbeeld van de door getOperation geretourneerde bewerking, terwijl applyOperation op regel 25 de bewerking daadwerkelijk op ons document toepast.
getOperation moet zo worden geïmplementeerd dat het de bewerking retourneert die als voorbeeld moet worden getoond of op het document moet worden toegepast. Dit is iets complexer dan wat we hierboven met de simple API hebben gezien. Dit komt doordat één enkele bewerking kan worden gebruikt om meerdere objecten toe te voegen, objecten te wijzigen of objecten te verwijderen.
Widgets aan de optiebalk toevoegen
Section titled “Widgets aan de optiebalk toevoegen”De in ons voorbeeld getekende cirkel heeft altijd een straal van 1 tekeningeenheid (zie regel 33). In een volgende stap willen we de gebruiker toestaan een straal voor de cirkel in te voeren. QCAD gebruikt gewoonlijk de optiebalk bovenaan om zulke gereedschapsparameters weer te geven en te wijzigen. Hiervoor moeten we definiëren welke widgets we in de optiebalk willen tonen en welke parameters ze regelen. Dit kan met een UI-bestand, een XML-bestand dat een widget en zijn inhoud definieert. UI-bestanden kunnen comfortabel worden ontworpen met een programma genaamd Qt Designer, dat deel uitmaakt van de Qt-toolkit. Voor dit voorbeeld gebruiken we een eenvoudig UI-bestand dat ook in een teksteditor kan worden gemaakt (bestand 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>&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>Het UI-bestand definieert twee widgets: een label (QLabel) en een invoerveld (RMathLineEdit). Belangrijk is de naam van het invoerveld (“Radius”). Het widget wordt via deze naam automatisch aan ons script gekoppeld. Het enige wat we in ons script moeten doen, is definiëren welk UI-bestand we willen gebruiken (regel 9) en een nieuwe gebeurtenishandler implementeren met de naam slotRadiusChanged, dat wil zeggen “slot” + [de naam van ons invoerveld] + “Changed” (regel 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"]);};Deze nieuwe functie slotRadiusChanged wordt aangeroepen telkens wanneer de gebruiker een nieuwe straal invoert. Ze stelt de lidvariabele this.radius in, die op haar beurt wordt gebruikt bij het maken van de cirkel in getOperation.
Alle scripts in QCAD zijn gebaseerd op een van de in deze tutorial beschreven concepten.
Aangezien elk gereedschap in QCAD op het hoogste niveau als een script is geïmplementeerd, zijn er tal van voorbeeldscripts beschikbaar. U vindt ze in ons git-repository.