Interaktive Skriptaktionen
Einführung
Abschnitt betitelt „Einführung“Skriptaktionen sind Skripte, die einem Menü und/oder einer Werkzeugleiste einen Eintrag hinzufügen und Benutzerinteraktionen verarbeiten können. Skriptaktionen bleiben aktiv, bis sie vom Benutzer beendet werden oder bis sie sich selbst beenden.
Ereignisse
Abschnitt betitelt „Ereignisse“Sobald die Skriptaktion gestartet ist, verarbeitet sie verschiedene Ereignisse, bis sie beendet wird. Ein Ereignis ist etwas, das eintritt, wenn etwas geschieht. Wird die Skriptaktion zum Beispiel gestartet, wird beginEvent aufgerufen. Klickt der Benutzer auf ein Objekt, wird ein pickEntity-Ereignis ausgelöst, klickt der Benutzer auf eine Koordinate, tritt ein pickCoordinate-Ereignis auf usw.
Die minimale Struktur einer Skriptaktion sieht wie folgt aus:
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"]);};Dieses Beispielskript fügt am unteren Ende des Menüs Diverses > Beispiele ein Menü hinzu. Der Menütext lautet “Minimal Example”.
Beachten Sie, dass der Dateiname mit dem Klassennamen übereinstimmen muss, damit das Skript gefunden wird, in diesem Fall also “ExMyMinimal.js”. Es muss zudem in einem Verzeichnis mit demselben Namen “ExMyMinimal” liegen, sodass dieses Skript zum Beispiel unter scripts/Misc/ExMyMinimal/ExMyMinimal.js abgelegt werden kann.
Sie können Ihre Skripte auch in einem lokalen Skriptordner in Ihrem Benutzerverzeichnis ablegen. Um den genauen Ordner herauszufinden, öffnen Sie den Über-Dialog (Hilfe > Über QCAD…) und wechseln Sie zum Reiter System. Dort sehen Sie den Speicherort der Daten unter Data directory. Unter diesem Verzeichnis müssen Sie einen Unterordner mit dem Namen scripts anlegen und darin weitere Unterordner, einen Ordner pro Skriptwerkzeug, zum Beispiel /Pfad/zum/Datenverzeichnis/scripts/MyScripts/MyScript1/MyScript1.js
Der genaue Speicherort hängt von Ihrem System und dessen Konfiguration ab.
beginEvent hinzufügen
Abschnitt betitelt „beginEvent hinzufügen“Das obige Skript ist voll funktionsfähig und kann ausgelöst werden. Allerdings tut es beim Auslösen nichts. Zudem bleibt das Skript nach dem Auslösen aktiv, bis der Benutzer es durch Klicken der rechten Maustaste beendet. Um dies zu ändern, implementieren wir beginEvent so, dass es etwas in die Befehlszeilen-Historie von QCAD ausgibt und die Aktion beendet:
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"]);};Wird das Werkzeug Diverses > Beispiele > Minimal Example nun gestartet, gibt es “Hello World!” in die Befehlszeilen-Historie aus (Zeile 12) und beendet sich anschließend (Zeile 14).
Erfordert ein Skript keinerlei Benutzerinteraktion, kann ein solches Skript verwendet werden, um ein Menü hinzuzufügen, das etwas ausführt und sich dann beendet. Beispiele für solche Aktionen sind Ansicht > Auto Ansicht, Selektion > Alles selektieren, Bearbeiten > Löschen usw.
Interaktion hinzufügen
Abschnitt betitelt „Interaktion hinzufügen“Sobald ein Skript irgendeine Form von Benutzerinteraktion erfordert, müssen wir weitere Ereignishandler implementieren und dem Skript mitteilen, was der Benutzer als Nächstes tun soll (z. B. ein Objekt wählen oder eine Koordinate festlegen). Im nächsten Schritt versetzen wir es in einen Zustand, in dem die Aktion eine Koordinate vom Benutzer erwartet. Anschließend zeichnen wir an jeder Position, die der Benutzer anklickt oder eingibt, einen Kreis.
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"]);};Im beginEvent beenden wir die Aktion nicht mehr sofort, sondern lassen sie laufen, bis der Benutzer sie beendet (Rechtsklick oder Escape). Anschließend implementieren wir pickCoordinate, um die Position des Mauszeigers oder die eingegebene Koordinate zu speichern und entweder die Vorschau zu aktualisieren oder die Operation anzuwenden (d. h. den Kreis hinzuzufügen). pickCoordinate wird immer dann aufgerufen, wenn der Benutzer die Maus bewegt, um eine Vorschau der geplanten Operation anzuzeigen. Wenn der Benutzer klickt oder eine Koordinate eingibt, wird es mit dem Parameter preview gleich false aufgerufen, um anzuzeigen, dass eine endgültige Koordinate gewählt oder eingegeben wurde.
updatePreview in Zeile 22 zeigt eine Vorschau der von getOperation zurückgegebenen Operation an, während applyOperation in Zeile 25 die Operation tatsächlich auf unser Dokument anwendet.
getOperation muss so implementiert werden, dass es die Operation zurückgibt, die als Vorschau angezeigt oder auf das Dokument angewendet werden soll. Dies ist etwas komplexer als das, was wir oben in der Simple API gesehen haben. Der Grund ist, dass eine einzelne Operation dazu verwendet werden kann, mehrere Objekte hinzuzufügen, Objekte zu modifizieren oder Objekte zu löschen.
Widgets zur Optionsleiste hinzufügen
Abschnitt betitelt „Widgets zur Optionsleiste hinzufügen“Der in unserem Beispiel gezeichnete Kreis hat immer einen Radius von 1 Zeichnungseinheit (siehe Zeile 33). In einem nächsten Schritt möchten wir dem Benutzer erlauben, einen Radius für den Kreis einzugeben. QCAD verwendet üblicherweise die Optionsleiste am oberen Rand, um solche Werkzeugparameter anzuzeigen und zu ändern. Dazu müssen wir festlegen, welche Widgets wir in der Optionsleiste anzeigen möchten und welche Parameter sie steuern. Dies kann mit einer UI-Datei erfolgen, einer XML-Datei, die ein Widget und dessen Inhalt definiert. UI-Dateien lassen sich bequem mit einer Software namens Qt Designer gestalten, die Teil des Qt-Toolkits ist. Für dieses Beispiel verwenden wir eine einfache UI-Datei, die sich auch in einem Texteditor erstellen lässt (Datei 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>Die UI-Datei definiert zwei Widgets: ein Label (QLabel) und ein Eingabefeld (RMathLineEdit). Wichtig ist der Name des Eingabefelds (“Radius”). Über diesen Namen wird das Widget automatisch mit unserem Skript verknüpft. Wir müssen in unserem Skript lediglich festlegen, welche UI-Datei wir verwenden möchten (Zeile 9), und einen neuen Ereignishandler namens slotRadiusChanged implementieren, das heißt “slot” + [der Name unseres Eingabefelds] + “Changed” (Zeile 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"]);};Diese neue Funktion slotRadiusChanged wird immer dann aufgerufen, wenn der Benutzer einen neuen Radius eingibt. Sie setzt die Membervariable this.radius, die wiederum beim Erstellen des Kreises in getOperation verwendet wird.
Alle Skripte in QCAD basieren auf einem der in diesem Tutorial dargestellten Konzepte.
Da jedes Werkzeug in QCAD auf oberster Ebene als Skript implementiert ist, stehen zahlreiche Beispielskripte zur Verfügung. Sie finden diese in unserem Git-Repository.