Gå til indhold

Interaktive scripthandlinger

Scripthandlinger er scripts, der tilføjer en post til en menu og/eller værktøjslinje, og som kan håndtere brugerinteraktioner. Scripthandlinger forbliver aktive, indtil de afsluttes af brugeren, eller indtil de selv afslutter.

Så snart scripthandlingen startes, håndterer den forskellige hændelser, indtil den afsluttes. En hændelse er noget, der opstår, hvis noget sker. For eksempel, hvis scripthandlingen startes, kaldes beginEvent. Hvis brugeren klikker på et objekt, udløses en pickEntity-hændelse, hvis brugeren klikker på en koordinat, opstår en pickCoordinate-hændelse osv.

Den minimale struktur af en scripthandling er som følger:

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

Dette eksempelscript tilføjer en menu i bunden af menuen Diverse > Eksempler. Menuteksten er “Minimal Example”.

Bemærk, at for at scriptet kan findes, skal filnavnet matche klassenavnet, dvs. “ExMyMinimal.js” i dette tilfælde. Det skal også ligge i en mappe med samme navn “ExMyMinimal”, så dette script kan for eksempel placeres i scripts/Misc/ExMyMinimal/ExMyMinimal.js.

Du kan også placere dine scripts i en lokal scripts-mappe i din brugermappe. For at finde den præcise mappe skal du åbne Om-dialogboksen (Hjælp > Om QCAD…) og gå til fanen System. Der kan du se dataplaceringen under Data directory. Dette er den mappe, hvorunder du skal oprette en undermappe med navnet scripts og deri undermapper, én mappe pr. scriptværktøj, for eksempel /sti/til/datamappe/scripts/MyScripts/MyScript1/MyScript1.js

Den præcise placering afhænger af dit system og dets konfiguration.

Scriptet ovenfor er fuldt funktionelt og kan udløses. Det gør dog ikke rent faktisk noget, når det udløses. Desuden forbliver scriptet, når det først er udløst, aktivt, indtil brugeren afslutter det ved at klikke på højre museknap. For at ændre dette, lad os implementere beginEvent til at udskrive noget til kommandolinjehistorikken i QCAD og afslutte handlingen:

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

Hvis værktøjet Diverse > Eksempler > Minimal Example nu startes, udskriver det “Hello World!” til kommandolinjehistorikken (linje 12) og afslutter derefter (linje 14).

Hvis et script ikke kræver nogen brugerinteraktion, kan et sådant script bruges til at tilføje en menu, der gør noget og derefter afslutter. Eksempler på sådanne handlinger er Visning > Autozoom, Vælg > Vælg alt, Rediger > Slet osv.

Så snart et script kræver nogen form for brugerinteraktion, skal vi implementere flere hændelseshåndteringer og fortælle scriptet, hvad brugeren skal gøre næst (f.eks. vælge et objekt eller definere en koordinat). I næste trin går vi ind i en tilstand, hvor handlingen forventer en koordinat fra brugeren. Vi tegner derefter en cirkel på hver position, brugeren klikker på eller indtaster.

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

I beginEvent afslutter vi ikke længere handlingen med det samme, men lader den køre, indtil brugeren afslutter den (højreklik eller Escape). Vi implementerer derefter pickCoordinate til at gemme positionen af musemarkøren eller den indtastede koordinat og til enten at opdatere forhåndsvisningen eller anvende operationen (dvs. tilføje cirklen). pickCoordinate kaldes, hver gang brugeren bevæger musen for at vise en forhåndsvisning af den planlagte operation. Når brugeren klikker eller indtaster en koordinat, kaldes den med parameteren preview sat til false for at indikere, at en endelig koordinat er valgt eller indtastet.

updatePreview på linje 22 forhåndsviser operationen returneret af getOperation, mens applyOperation på linje 25 rent faktisk anvender operationen på vores dokument.

getOperation skal implementeres til at returnere operationen, der skal forhåndsvises eller anvendes på dokumentet. Dette er lidt mere komplekst end det, vi har set i det simple API ovenfor. Dette skyldes, at en enkelt operation kan bruges til at tilføje flere objekter, ændre objekter eller slette objekter.

Tilføjelse af widgets til indstillingsværktøjslinjen

Sektion kaldt “Tilføjelse af widgets til indstillingsværktøjslinjen”

Cirklen tegnet i vores eksempel har altid en radius på 1 tegneenhed (se linje 33). I et næste trin vil vi tillade brugeren at indtaste en radius for cirklen. QCAD bruger normalt indstillingsværktøjslinjen øverst til at vise og ændre sådanne værktøjsparametre. Til dette skal vi definere, hvilke widgets vi vil vise i indstillingsværktøjslinjen, og hvilke parametre de styrer. Dette kan gøres med en UI-fil, en XML-fil, der definerer en widget og dens indhold. UI-filer kan bekvemt designes ved hjælp af en software kaldet Qt Designer, som er en del af Qt-værktøjssættet. Til dette eksempel bruger vi en simpel UI-fil, der også kan oprettes i en teksteditor (fil 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>

UI-filen definerer to widgets: en etiket (QLabel) og et linjeredigeringsfelt (RMathLineEdit). Vigtigt er navnet på linjeredigeringsfeltet (“Radius”). Widgeten linkes automatisk til vores script gennem dette navn. Alt, hvad vi skal gøre i vores script, er at definere, hvilken UI-fil vi vil bruge (linje 9), og at implementere en ny hændelseshåndtering kaldet slotRadiusChanged, det vil sige “slot” + [navnet på vores linjeredigeringsfelt] + “Changed” (linje 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"]);
};

Denne nye funktion slotRadiusChanged kaldes, hver gang brugeren indtaster en ny radius. Den sætter medlemsvariablen this.radius, som til gengæld bruges, når cirklen oprettes i getOperation.

Alle scripts i QCAD er baseret på et af disse koncepter beskrevet i denne vejledning.

Da hvert værktøj i QCAD er implementeret som et script på øverste niveau, er der masser af eksempelscripts tilgængelige. Du kan finde dem i vores git-repository.