Skip to content

互動式指令碼動作

指令碼動作是向功能表和/或工具列新增項目並可處理使用者互動的指令碼。指令碼動作會保持作用中,直到被使用者終止或直到它們自行終止。

指令碼動作一啟動,就會處理各種事件,直到它被終止。事件是指若有某事發生時所出現的事情。例如,若指令碼動作啟動,會呼叫 beginEvent。若使用者按一下實體,會觸發 pickEntity 事件,若使用者按一下座標,會發生 pickCoordinate 事件,依此類推。

指令碼動作的最小結構如下:

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

此範例指令碼在功能表 雜項 > 範例 底部新增一個功能表。功能表文字為 “Minimal Example”。

請注意,為了能找到指令碼,檔案名稱必須與類別名稱相符,即在此例中為 “ExMyMinimal.js”。它還必須位於相同名稱 “ExMyMinimal” 的目錄內,因此此指令碼例如可以放入 scripts/Misc/ExMyMinimal/ExMyMinimal.js。

您也可以將指令碼放入使用者家目錄中的本機 scripts 資料夾。若要得知確切的資料夾,請開啟關於對話框(說明 > 關於 QCAD…)並切換到 系統 分頁。您可以在其中的 Data directory 處看到資料位置。這就是您必須在其下建立名為 scripts 的子資料夾的目錄,並在其中為每個指令碼工具各建立一個子資料夾,例如 /資料目錄路徑/scripts/MyScripts/MyScript1/MyScript1.js

確切位置取決於您的系統及其設定。

上面的指令碼完全可運作,並且可以觸發。然而,觸發時它實際上不做任何事。此外,一旦觸發,指令碼會保持作用中,直到使用者按一下滑鼠右鍵將其終止。為了改變這一點,讓我們實作 beginEvent,向 QCAD 的命令列歷程列印一些內容並終止該動作:

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

如果現在啟動工具 雜項 > 範例 > Minimal Example,它會向命令列歷程列印 “Hello World!”(第 12 行),然後終止(第 14 行)。

如果指令碼不需要任何使用者互動,此類指令碼可用於新增一個做某事然後終止的功能表。此類動作的範例有 檢視 > 自動縮放、選取 > 全選、編輯 > 刪除 等。

指令碼一旦需要任何種類的使用者互動,我們就需要實作更多事件處理常式,並告訴指令碼使用者接下來需要做什麼(例如選取實體或定義座標)。在下一步中,我們進入一個動作預期使用者提供座標的狀態。然後我們在使用者按一下或輸入的每個位置繪製一個圓。

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

在 beginEvent 中,我們不再立即終止該動作,而是讓它執行直到使用者終止它(按右鍵或 Escape)。然後我們實作 pickCoordinate 以儲存滑鼠游標的位置或輸入的座標,並更新預覽或套用操作(即新增圓)。每當使用者移動滑鼠以顯示所計劃操作的預覽時,就會呼叫 pickCoordinate。當使用者按一下或輸入座標時,會以參數 preview 設為 false 呼叫它,以指示已選取或輸入確定的座標。

第 22 行的 updatePreview 預覽 getOperation 傳回的操作,而第 25 行的 applyOperation 實際將操作套用到我們的文件。

必須實作 getOperation 以傳回要預覽或套用到文件的操作。這比我們在上面簡單 API 中看到的稍微複雜一些。這是因為單一操作可用於新增多個物件、修改物件或刪除物件。

我們範例中繪製的圓的半徑始終為 1 個繪圖單位(見第 33 行)。在下一步中,我們想讓使用者輸入圓的半徑。QCAD 通常使用頂部的選項工具列來顯示和變更此類工具參數。為此,我們需要定義要在選項工具列中顯示哪些小工具以及它們控制哪些參數。這可以透過 UI 檔案來完成,UI 檔案是定義小工具及其內容的 XML 檔案。UI 檔案可以使用一個名為 Qt Designer 的軟體方便地設計,該軟體作為 Qt 工具組的一部分提供。對於此範例,我們使用一個也可以在文字編輯器中建立的簡單 UI 檔案(檔案 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 檔案定義兩個小工具:一個標籤(QLabel)和一個單行編輯欄(RMathLineEdit)。重要的是單行編輯欄的名稱(“Radius”)。小工具透過此名稱自動連結至我們的指令碼。我們在指令碼中要做的就是定義我們要使用哪個 UI 檔案(第 9 行),並實作一個名為 slotRadiusChanged 的新事件處理常式,即 “slot” + [我們單行編輯欄的名稱] + “Changed”(第 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"]);
};

每當使用者輸入新的半徑時,就會呼叫這個新函式 slotRadiusChanged。它設定成員變數 this.radius,而該變數又會在 getOperation 中建立圓時使用。

QCAD 中的所有指令碼都基於本教學中概述的其中一個概念。

由於 QCAD 中的每個工具在最上層都以指令碼形式實作,因此有大量可用的範例指令碼。您可以在我們的 git 儲存庫中找到它們。