Инструменты пользователя

Инструменты сайта


Боковая панель

Если вам не пришло письмо с регистрационными данными, проверьте папку «Спам»

Новости:

09 мая 2020
* Новая версия DataExpress



Обратная связь

Wiki-синтаксис
Как написать хорошую статью
Полигон

programmirovanie_v_dx:razrabotka_rasshirenij

25. Разработка расширений

В DataExpress расширения бывают двух видов: функции и действия. Они разрабатываются средствами DataExpress и представляют собой скрипты на Pascal Script. В редакторе скриптов создается модуль расширений, пишется скрипт, затем используется в текущей базе или экспортируется для использования в других базах. В одном модуле могут быть расширения обоих видов. Расширения позволяют непрограммистам использовать дополнительный функционал.

Описание модуля

В специальном комментарии {@module .. @} пишется автор, версия и описание модуля расширений.

{@module 
Author=Автор 
Version=1.0
Description=Назначение модуля 
@}

Всего 3 поля:

  • Author – информация об авторе.
  • Version – версия модуля.
  • Description – краткое описание модуля. Описание может быть записано в несколько строк.

Данный комментарий необязательный, но если он указывается, то нужно строго соблюдать состав и последовательность полей: author, version, description. Информация о модуле отображается в окне «Расширения».

Рис 20

Функции

Этот вид расширений позволяет дополнить выражения новыми функциями. В специальном комментарии {@function … @} пишется спецификация функции.

{@function
OrigName=GetFormName
Name=FormName
Args=
Result=s
Group=Мои функции
Description=Эта функция возвращает имя текущей формы @}
 
function GetFormName: String; 
begin
  Result := Self.FormCaption;
end;
 
{@function
OrigName=ToWordsBel
Name=ToWordsBel
Args=n
Result=s
Group=Функции преобразования
Description=Эта функция возвращает число прописью на белорусском языке. @}
 
function ToWordsBel(N: Double): String; 
beginend;

В комментарии должны быть следующие параметры:

  • OrigName – исходное имя функции в модуле.
  • Name – имя функции, используемое в выражениях.
  • Args – типы аргументов:
    • n – число,
    • s – строка,
    • d – дата,
    • t – время,
    • b – логический,
    • v – любой тип.

Записывается, как несколько идущих подряд символов: nns, s, ds и т. д.

  • Result – тип результата. Используются те же символы, что и в Args.
  • Group – название группы, в которой будет находиться функция в окне «Функции».
  • Description – описание функции. Отображается в окне «Функции». Описание может быть записано в несколько строк. Можно использовать некоторые html-теги.

Нужно строго соблюдать состав и последовательность полей. В случае нарушения правил, компилятор выдаст ошибку. Комментарии могут находиться в любом месте модуля, но рекомендуется писать их перед функцией. Возможной проблемой является дублирование имен функций разными разработчиками модулей. Поэтому старайтесь давать функциям уникальные имена, например, добавляя какой-нибудь префикс: EX_EXEC, MY_FUNC, DA_FIRMGET и т. д.

Разработчик модуля может тут же проверить работу функций. Достаточно просто запустить компиляцию. Программа выполнит разбор специальных комментариев и обновит сведения о функциях. После компиляции можно открыть редактор выражений и проверить синтаксис и описание функций.

В модулях выражений можно использовать переменную Self для доступа к текущей форме. Но нужно быть осторожным. Выражения могут выполняться не только в формах, но и в отчетах. Если выражение выполняется в отчете, то переменная Self = nil.

Действия

Действие описывается в специальных комментариях. Пример:

{@action
Id=D569B5E1-7575-445F-8DFB-54B5FF99595B
Target=button
OrigName=MyActionProc
Name=Некое действие
Group=Моя группа
UI=
<ui>
  <form name="form" caption="Выберите форму" />
  <field name="field" caption="Выберите поле" source="form" filter="number" />
  <grid name="grid" caption="Таблица" height="100" >
    <text name="text" caption="Название" />
    <checkbox name="checkbox" caption="Флажок" defaultvalue="1" />
  </grid>
  <expr name="expr" caption="Выражение" />
</ui>
Description=Описание действия
@}
procedure MyActionProc(FormName, FieldName: Variant; Grid: TVariantArray2d; Expr: Variant);
begin
end;

Параметры:

  • Id - уникальный идентификатор действия. Программа ищет действие по его идентификатору. Это любая последовательность символов, но лучше использовать для этого GUID. В редакторе скриптов GUID можно вставить комбинацией Ctrl-Shift-G.
  • Target - определяет, где можно применять действие: в форме, кнопке, при запуске базы или везде. В редакторе скриптов в комментариях @action должен идти после атрибута id. Допустимые значения соответственно: form, button, main, all. При выборе действия в списке будут только допустимые действия.
  • OrigName - имя процедуры или функции в модуле.
  • Name - название действия для пользователя.
  • Group - группа, к которой принадлежит действие. Сочетание группы и названия должно быть уникальным.
  • UI - описание интерфейса в формате XML. Компоненты используются для настройки действия.
  • Description - описание действия.

Описание интерфейса. Параметр UI

Для описания интерфейса действия служит ряд тегов. Каждый тег соответствует определенному компоненту:

  • text - поле для ввода текста;
  • number - поле для ввода числа;
  • checkbox - флажок;
  • file - поле с кнопкой выбора файла;
  • folder - поле с кнопкой выбора папки;
  • expr - поле для ввода выражения;
  • filter - поле для ввода выражения фильтра;
  • list - список значений;
  • form - список основных форм (без подчиненных);
  • childform - список подчиненных форм;
  • query - список запросов;
  • object - список объектов;
  • field - список полей;
  • component - список компонентов;
  • report - список отчетов;
  • template - список шаблонов;
  • color - цвет;
  • image - изображение из галереи;
  • divider - разделитель;
  • grid - таблица.

Теги имеют атрибуты:

  • name - имя компонента;
  • caption - поясняющая надпись;
  • height - высота таблицы;
  • width - ширина столбца таблицы;
  • required - обязательное;
  • defaultvalue - значение по умолчанию;
  • items - элементы списка;
  • source - компонент-источник;
  • filter - фильтр списка.

Каждый компонент должен иметь обязательный атрибут name - имя компонента (для разделителя необязательно). Имя должно содержать только символы латинского алфавита, цифры и символ подчеркивания. Имя не может начинаться с цифры, должно быть уникальным в пределах интерфейса.

Элементы списка items отделяются друг от друга точкой с запятой: один;два;три.

Такие компоненты, как expr, childform, query, object, field, component, могут зависеть от другого компонента. Имя этого компонента указывается в атрибуте source.

Атрибут filter используется в тегах: file, field и components.

Атрибуты в тегах

Рассмотрим некоторые атрибуты применительно к тегам.

Filter в теге file

В данном случае filter определяет фильтр диалогового окна выбора файла. Синтаксис его точно такой же, как в компонентах TOpenDialog и TSaveDialog. Пример:

<file name="file" caption="Запустить программу" filter="Приложения|*.exe" />

Source в теге expr

Определяет форму (form), в контексте которой вычисляется выражение. Если атрибут не задан, то используется текущая форма. Пример:

<form name="myform" caption="Форма" />
<expr name="myexpr" caption="Выражение" source="myform" />

Source в теге filter

Определяет форму (form), данные которой нужно отфильтровать. Если атрибут не задан, то используется текущая форма.

Source в теге form

Может определять запрос (query), формы которого показать в списке. Если атрибут не указан, то список содержит все основные формы.

Source в теге childform

Может определять форму (form) или объект (object). Если указан объект, то в списке будут все подчиненные формы той формы, на которую ссылается объект. Если источник не указан, то используется текущая форма.

Source в теге query

Может определять форму (form), подчиненную форму (childform) или объект (object). Атрибут определяет запросы какой формы будут в списке. Если источник не указан, то используется текущая форма.

Source в теге object

Может определять форму или подчиненную форму. Если атрибут не задан, то используется текущая форма.

Source в теге field

Может определять форму, подчиненную форму, объект или запрос. Если источник не указан, то используется текущая форма. Атрибут filter определяет поля какого типа показать в списке. Типы отделяются точкой с запятой. Возможные значения:

  • text - текстовые поля (текст, список, заметка),
  • number - число,
  • date - дата,
  • time - время,
  • object - объект,
  • counter - счетчик,
  • checkbox - флажок,
  • file - файл,
  • image - изображение.

Пример:

<form name="myform" caption="Форма" />
<field name="myfield" caption="Поле" source="myform" filter="text;number;date" />

Source в теге components

Может определять форму или подчиненную форму. Если источник не задан, то используется текущая форма. Атрибут filter определяет компоненты какого типа показать в списке. Типы перечисляются через точку с запятой. В качестве типа используется имя класса. Пример:

<form name="myform" caption="Форма" />
<component name="mycmp" caption="Компонент" source="myform" filter="TdxEdit;TdxComboBox;TdxLabel;TdxButton" />

Зависимость может иметь несколько уровней.

Пример:

<form name="myform" caption="Форма" />
<childform name="mychildform" caption="Таблица" source="myform" />
<object name="obj" caption="Объект" source="mychildform" />
<childform name="objchild" caption="Таблица объекта" source="obj" />
<field name="field" caption="Поле" source="objchild" />

Если в родительском компоненте меняется значение, то подчиненные компоненты очищаются. Это относится только к спискам.

Атрибут items

Используется в паре с тегом list и определяет элементы списка. Элементы перечисляются через точку с запятой.

Атрибут required

Можно включить в любой тег (правда, для grid он не имеется смысла). При сохранении программа выдаст сообщение, если какой-то из обязательных параметров не заполнен.

Атрибут defaultvalue

Определяет значение по умолчанию для компонента. Для флажка возможно единственное значение: 1. Остальные значения будут проигнорированы.

Атрибут caption

Определяет поясняющую надпись. Для разделителя также можно указать надпись.

Атрибут noform

Используется в теге expr. Если прописать noform="1", то в редакторе выражений нельзя будет выбрать поле формы.

Таблицы

Вы можете задавать не просто отдельные компоненты, а целые массивы компонентов. Для этого используются таблицы. Синтаксис очень простой. Чтобы определить столбцы и типы компонентов, достаточно вышеперечисленные теги перечислить между тегами <grid>..</grid>.

Пример:

<form name="myform" caption="Форма" />
<grid name="fields" caption="Поля" height="150">
  <field name="field" caption="Поле" source="myform" filter="text" />
  <checkbox name="mychk" caption="Флажок" width="80" />
</grid>

Таблице можно задать высоту с помощью атрибута height. Текст заголовков берется из атрибута caption вложенных тегов. Если ширина width не задана, то ширина колонки определяется автоматически. Ширину колонок менять нельзя. Если в таблице определен только один компонент, то заголовок таблицы скрывается. Если в таблице определены зависимые компоненты, то при изменении значения в родительском компоненте ячейки НЕ стираются. Символы внутри атрибутов <, > и « должны быть заменены на &lt; &gr; &quot; соответственно.

Сопоставление компонентов с параметрами процедуры

Число параметров в процедуре должно совпадать с количеством компонентов, не включая компоненты в тегах grid и разделители divider.

Каждый параметр должен иметь тип Variant. Параметр для тега grid должен иметь тип TVariantArray2d, который представляет собой двухмерный массив: array of array of Variant. Первое измерения - это строки, второе - столбцы. Параметры и элементы массива могут быть двух типов: LongInt (компоненты number, checkbox, color) и String (остальные). Параметр может быть равен NULL, если компонент был добавлен позже, чем были сохранены параметры.

Новые версии действий

Разработчик может постепенно совершенствовать действия, добавляя новые компоненты. Допускается менять порядок компонентов и столбцов в таблице. Программа сопоставляет компоненты с параметрами по имени компонента. Также допустимо изменять название действия и группу, программа определяет действие по его ID. Вот ID менять нельзя, т. к. тогда все настройки действия могут быть потеряны при открытии окна «Действие».

Действия формы

Действия формы начинают свое выполнение при создании формы. В принципе, на этом этапе их работа может быть закончена. Но это относится только к простейшим действиям, которые, например, устанавливают свойство компонента. Основное же назначение таких действий – подключаться к событиям формы и других компонентов и выполняться при наступлении этих событий. Действие добавляются в свойстве формы «Действия формы».

Действие формы сложнее разработать, чем действие кнопки, потому что приходиться заботиться о сохранении параметров функции в каких-либо структурах для того, чтобы воспользоваться ими затем при наступлении события, к которому подключилось действие. Кроме того необходимо сохранять старые обработчики событий и вызывать их в новых, чтобы не нарушить работу скрипта и других расширений. Пример подключаемого действия:

{@action
Id=BE798939-0563-4DC7-8789-BBB636BD45FA
Target=form
OrigName=VisibleByCondition
Name=Видимость компонентов по условию
Group=DEMO
UI=<ui>
  <expr name="condition" caption="Условие" required="1"/>
  <grid name="grid" caption="Компоненты">
    <component name="cmp" required="1"/>
  </grid>
</ui>
Description=Обеспечивает видимость компонентов по условию. Компонент видимый,
если условие истинно. Пример условия:<br><br>
<сode>[дата]<>null</сode><br><br>
Это подключаемое действие.
@}
 
type
  TVCRec = record
    Fm: TdxForm;
    Expr: String;
    Cmps: TVariantArray2d;
    AfterScroll, AfterCancel: TNotifyEvent;
    FieldChange: TFieldChangeEvent;
  end;
 
var
  VCData: array of TVCRec;
  VC_OldDBClose: TNotifyEvent;
 
// Обработчик события закрытия базы данных
procedure VC_DBClose(Sender: TObject);
begin
  SetLength(VCData, 0);
  if VC_OldDBClose <> nil then VC_OldDBClose(Sender);
end;
 
// Поиск нужного элемента массива по форме
function VC_GetIndex(Fm: TObject): Integer;
var
  i: Integer;
begin
  Result := -1;
  for i := 0 to Length(VCData) - 1 do
    if VCData[i].Fm = Fm then
    begin
      Result := i;
      Exit;
    end;
end;
 
// Само действие
procedure VC_SetVisible(var VC: TVCRec);
var
  b: Boolean;
  i: Integer;
  C: TControl;
begin
  // Результат вычисления выражения должен быть логического типа
  b := EvalExpr(VC.Expr, VC.Fm);
  for i := 0 to Length(VC.Cmps) - 1 do
  begin
    // Находим компонент по имени (в Cmps хранятся имена компонентов).
    C := TControl( VC.Fm.FindComponent(VC.Cmps[i][0]) );
    if C <> nil then C.Visible := b;
  end;
end;
 
// Действие при переходе на запись
procedure VC_AfterScroll(Sender: TObject);
var
  VC: TVCRec;
begin
  // Находим наш элемент
  VC := VCData[VC_GetIndex(Sender)];
  // Вызываем старый обработчик события, если он есть
  if VC.AfterScroll <> nil then VC.AfterScroll(Sender);
  // Само действие
  VC_SetVisible(VC);
end;
 
// Действие при отмене изменений записи
procedure VC_AfterCancel(Sender: TObject);
var
  VC: TVCRec;
begin
  VC := VCData[VC_GetIndex(Sender)];
  if VC.AfterCancel <> nil then VC.AfterCancel(Sender);
  VC_SetVisible(VC);
end;
 
// Действие при изменении поля
procedure VC_FieldChange(Sender, Control: TObject; const FieldName: String);
var
  VC: TVCRec;
begin
  VC := VCData[VC_GetIndex(Sender)];
  if VC.FieldChange <> nil then VC.FieldChange(Sender, Control, FieldName);
  VC_SetVisible(VC);
end;
 
// Эту функцию вызывает программа, когда выполняет подключаемое действие.
function VisibleByCondition(aExpr: String; aCmps: TVariantArray2d): Boolean;
var
  i: Integer;
begin
  Result := True;
 
  i := Length(VCData);
  // При первом использовании действия устанавливаем обработчик закрытия базы,
  // чтобы можно было выполнить какие-либо завершающие действия, например,
  // освободить память.
  if i = 0 then
  begin
    VC_OldDBClose := MainWindow.OnDatabaseClose;
    MainWindow.OnDatabaseClose := @VC_DBClose;
  end;
  // Каждый вызов действия увеличивает массив на один элемент.
  SetLength(VCData, i+1);
  // Сохраняем в последний элемент все необходимое для дальнейшей работы действия,
  // подключаемся к событиям.
  with VCData[i] do
  begin
    Fm := Self;
    Expr := aExpr;
    Cmps := aCmps;
    AfterScroll := Self.OnAfterScroll;
    AfterCancel := Self.OnAfterCancel;
    FieldChange := Self.OnFieldChange;
    Self.OnAfterScroll := @VC_AfterScroll;
    Self.OnAfterCancel := @VC_AfterCancel;
    Self.OnFieldChange := @VC_FieldChange;
  end;
end;

Несмотря на относительную сложность, разработка действия формы ведется по шаблону:

1. Создаем структуру для хранения параметров функции, формы, старых обработчиков событий и других данных.

2. Создаем динамический массив для хранения этих структур.

3. При первом использовании действия, устанавливаем обработчик события закрытия базы, предварительно сохранив старый.

4. Увеличиваем размер массива на единицу и записываем в последний элемент необходимые данные.

5. Подключаемся к событиям.

6. Делаем функцию для поиска элемента массива по форме.

7. Создаем необходимые обработчики событий.

8. В обработчиках первым делом ищем нужный элемент массива. Если Sender обработчика не форма, а другой компонент, то добраться до формы можно через свойство Owner компонента или другим способом. В принципе, поиск элемента массива можно делать не по форме, а по другому компоненту.

9. Вызываем старый обработчик события, если он есть.

10. Выполняем необходимые действия.

В зависимости от сложности действия некоторых пунктов может не быть. Например, не всегда требуется перехватывать события существующих компонентов. Если создается новый компонент, то нет необходимости сохранять старый обработчик. Не всегда даже требуется создавать структуру и массив для их хранения.

Действия при запуске

Действие выполняется при подключении к базе данных. Этот тип действий разрабатывается по тем же правилам, что и действия формы. Действие может подключаться к событиям разных компонентов и может выполнять свои функции на протяжении всего сеанса работы с БД.

programmirovanie_v_dx/razrabotka_rasshirenij.txt · Последние изменения: 2020/03/15 22:02 — 7bit