Інтерактивні скриптові дії
Скриптові дії — це скрипти, які додають запис до меню та/або панелі інструментів і які можуть обробляти взаємодії з користувачем. Скриптові дії залишаються активними, доки їх не завершить користувач або доки вони не завершаться самі.
Щойно скриптова дія запускається, вона обробляє різні події, доки не буде завершена. Подія — це щось, що виникає, якщо щось відбувається. Наприклад, якщо скриптова дія запущена, викликається 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
Section titled “Додавання beginEvent”Наведений вище скрипт повністю функціональний і його можна запустити. Проте при запуску він насправді нічого не робить. Крім того, після запуску скрипт залишається активним, доки користувач не завершить його, клацнувши праву кнопку миші. Щоб це змінити, реалізуймо 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).
Якщо скрипт не потребує жодної взаємодії з користувачем, такий скрипт можна використовувати для додавання меню, яке щось робить, а потім завершується. Прикладами таких дій є Вид > Автомасштаб, Вибрати > Вибрати все, Редагувати > Видалити тощо.
Додавання взаємодії
Section titled “Додавання взаємодії”Щойно скрипт потребує будь-якого виду взаємодії з користувачем, нам потрібно реалізувати більше обробників подій і повідомити скрипту, що користувач має зробити далі (напр., вибрати об’єкт або визначити координату). На наступному кроці ми входимо в стан, у якому дія очікує координату від користувача. Потім ми креслимо коло в кожній позиції, на яку користувач клацає або яку вводить.
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, щоб указати, що остаточну координату вибрано або введено.
updatePreview у рядку 22 показує попередній перегляд операції, яку повертає getOperation, тоді як applyOperation у рядку 25 фактично застосовує операцію до нашого документа.
getOperation має бути реалізована так, щоб повертати операцію для попереднього перегляду або застосування до документа. Це трохи складніше, ніж те, що ми бачили в простому API вище. Це тому, що одну операцію можна використати для додавання кількох об’єктів, зміни об’єктів або видалення об’єктів.
Додавання віджетів до панелі інструментів параметрів
Section titled “Додавання віджетів до панелі інструментів параметрів”Коло, накреслене в нашому прикладі, завжди має радіус 1 креслярської одиниці (див. рядок 33). На наступному кроці ми хочемо дозволити користувачеві ввести радіус для кола. QCAD зазвичай використовує панель інструментів параметрів угорі для відображення та зміни таких параметрів інструмента. Для цього нам потрібно визначити, які віджети ми хочемо показати на панелі інструментів параметрів і які параметри вони контролюють. Це можна зробити за допомогою файлу 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>&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.