コンテンツにスキップ

対話的スクリプトアクション

スクリプトアクションは、メニューやツールバーに項目を追加し、ユーザーとの対話を処理できるスクリプトです。スクリプトアクションは、ユーザーによって終了されるか、自身で終了するまでアクティブなままです。

スクリプトアクションは開始されるとすぐに、終了するまでさまざまなイベントを処理します。イベントとは、何かが起きたときに発生するものです。たとえば、スクリプトアクションが開始されると 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 というサブフォルダを作成し、さらにスクリプトツールごとに1つのサブフォルダを作成する必要があります。例: /データディレクトリへのパス/scripts/MyScripts/MyScript1/MyScript1.js

正確な場所は、お使いのシステムとその構成によって異なります。

上記のスクリプトは完全に機能し、実行できます。ただし、実行しても実際には何も行いません。さらに、いったん実行されると、ユーザーがマウスの右ボタンをクリックして終了するまでアクティブなままです。これを変更するために、QCADのコマンドライン履歴に何かを出力してアクションを終了するようにbeginEventを実装しましょう。

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つの操作を使って複数のオブジェクトを追加したり、オブジェクトを変更したり、削除したりできるためです。

オプションツールバーへのウィジェットの追加

Section titled “オプションツールバーへのウィジェットの追加”

この例で描かれる円の半径は常に1図面単位です(33行目を参照)。次のステップでは、ユーザーが円の半径を入力できるようにします。QCADは通常、このようなツールのパラメータを表示・変更するために、上部のオプションツールバーを使用します。そのためには、オプションツールバーにどのウィジェットを表示し、それらがどのパラメータを制御するかを定義する必要があります。これはUIファイル、つまりウィジェットとその内容を定義するXMLファイルで行えます。UIファイルは、Qtツールキットの一部として提供されるQt Designerというソフトウェアを使って快適に設計できます。この例では、テキストエディタでも作成できる簡単な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ファイルは2つのウィジェットを定義します。ラベル(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リポジトリで見つけることができます。