Таблицы в генераторе отчетов ГИС Геопроект 5.x

Наверное, каждый, кто начинал разбираться с Генератором отчетов, задавался вопросом: «Как вставить в шаблон таблицу?». Этот вопрос звучит снова и снова, ведь таблицы являются одним из основных элементов разнообразных кадастровых и геодезических документов (достаточно вспомнить про ведомости координат, таблицы румбов, экспликации земель).

На первый взгляд, отсутствие стандартных средств, как в MS Word/OOo Writer, делает невозможным решение этой задачи. Но это только на первый взгляд… В действительности FastReport, а значит и Генератор отчетов, как минимум не уступают текстовым редакторам в вопросах создания таблиц.

Что есть таблица? Если отвлечься от реальности и немного пофантазировать, то любую таблицу можно представить себе как здание, построенное из отдельных «кирпичиков» — ячеек. А что представляет собой ячейка? Да ведь это просто текст, помещенный в рамочку. Решение найдено!

Действительно, любая таблица — это совокупность текстовых блоков с обрамлением. Текстовые блоки с обрамлением у нас уже есть — Memo (TfrxMemoView); эстеты могут использовать RichText (TfrxRichView). Осталось только разместить на странице нужное количество этих «кирпичиков», задать им размеры, включить рамку, наполнить текстом и «построить» таблицу. Чтобы результат нашей работы был больше похож на настоящую таблицу, можно сгруппировать все элементы в единый блок.

При этом, не смотря на жестко заданную форму, наша таблица способна на многое: ведь мы можем менять значения в ячейках прямо в процессе формирования отчета. Это могут быть как значения некоторой семантики объекта, так и результаты каких-то расчетов или манипуляций со строками. Примерами применения описанного подхода на практике служат такие стандартные шаблоны как «Техдокументация (Гос акт)» (последняя страница), «План со штампом 1» и «План со штампом 2» (собственно штамп и таблица экспликации); в старых версиях ГИС, кроме этих шаблонов были еще такие, как «Рег карточка (Сторона 1)», «Рег карточка (Сторона 2)» и «Рег карточка (Аренда)».

Обычные таблицы — это хорошо, но что делать, если количество строк в таблице заранее не известно? Неужели тупик и каждый раз придется вручную создавать таблицу с нужным количеством строк? Отнюдь. Как говаривал барон Мюнхгаузен: «Безвыходных ситуаций не бывает», а из этой ситуации есть целых два выхода. Сначала мы рассмотрим простой, не побоюсь сказать примитивный, вариант, а в конце — более изящный и в то же время более сложный.

Вариант № 1. Решение «в лоб»

Подходит не для всех случаев, но прост. Из недостатков отмечу:

  • большой объем кода, который необходим для реализации;
  • существенно возрастает объем шаблона;
  • неудобство в использовании в случае громоздких таблиц (более 5-10 строк и 3-4 столбцов).

Хотя в ранних шаблонах автора этот метод применялся достаточно широко и проблем не наблюдается до сих пор, рекомендую тщательно обдумать целесообразность применения такого подхода в своих документах.

Проще всего объяснить реализацию этого метода на примере. Пусть у нас есть некоторое количество участков, необходимо сформировать таблицу следующего вида

№ п/п Землепользователь Целевое назначение Площадь, га

Предположим, что максимальное количество участков, которое придется обрабатывать равно 10 (или любому другому не слишком большому числу). Т.е. нам известно максимальное количество строк в таблице. От этого числа и будем двигаться. Создаем таблицу нужного вида в расчете на максимальное количество записей. Столбец «№ п/п» можно сразу заполнить данными, так как эти значения не будут меняться при выборе других участков.
Для хранения данных заведем три массива (по одному на каждый столбец) и напишем код для сбора нужной информации:

var
  //землепользователь и целевое
  OwnerName, Target: array [1..10] of string;
  //площадь участка
  LandArea: array [1..10] of double;
  //служебные переменные
  i: integer;

begin
  //перебираем выбранные участки и сохраняем семантику в массивы
  for i:=0 to SelectCount-1 do begin
    OwnerName[i+1]:=Semantic(FindCode(i+1), ’0061’, 0);
    Target[i+1]:=Semantic(FindCode(i+1), ’0014’, 1);
    LandArea[i+1]:=Area(FindCode(i+1), 1);
  end;
end.

Теперь заполняем таблицу, подставляя в ячейки соответствующие переменные. Должно получиться что-то вроде этого

№ п/п Землепользователь Целевое назначение Площадь, га
1 [OwnerName[1]] [Target[1]] [LandArea[1]]
2 [OwnerName[2]] [Target[2]] [LandArea[2]]
10 [OwnerName[10]] [Target[10]] [LandArea[10]]

Если сейчас проверить шаблон, то увидим, что независимо от количества выбранных участков отображается вся таблица целиком, но количество строк с данными равно количеству выбранных участков. Осталось только скрыть пустые строки. Это можно делать каждый раз вручную, а можно переложить на плечи Генератора отчетов.

Чтобы не путаться, рекомендую дать ячейкам таблицы осмысленные названия, указывающие на столбец и строку: например, mNom1mNom10 — для порядкового номера; mOwner1mOwner10 — для названия землепользователя и т.д.

Скрывать лишние строки будем при помощи следующего кода (вставляем его сразу после цикла):

case SelectCount of
  1: begin
    //скрываем вторую строку и все что после нее
    mNom2.Visible:=false;
    mOwner2.Visible:=false;
    mTarget2.Visible:=false;
    mArea2.Visible:=false;
    mNom3.Visible:=false;
    mOwner3.Visible:=false;
    mTarget3.Visible:=false;
    mArea3.Visible:=false;
    …
    mNom10.Visible:=false;
    mOwner10.Visible:=false;
    mTarget10.Visible:=false;
    mArea10.Visible:=false;
  end;
  2: begin
    //скрываем третью строку и все что после нее
    mNom3.Visible:=false;
    mOwner3.Visible:=false;
    mTarget3.Visible:=false;
    mArea3.Visible:=false;
    mNom4.Visible:=false;
    mOwner4.Visible:=false;
    mTarget4.Visible:=false;
    mArea4.Visible:=false;
    …
    mNom10.Visible:=false;
    mOwner10.Visible:=false;
    mTarget10.Visible:=false;
    mArea10.Visible:=false;
  end;
  …
  9: begin
    //скрываем последнюю строку
    mNom10.Visible:=false;
    mOwner10.Visible:=false;
    mTarget10.Visible:=false;
    mArea10.Visible:=false;
  end;
end;

Надеюсь, этого достаточно, чтобы понять принцип и уловить закономерность. Поскольку изначально вся таблица видима, обрабатывать вариант с максимальным количеством строк нет смысла — в этом случае все будет отображаться и так.

Да, громоздко, неоптимально, некрасиво… Но, тем не менее, работает.

Вариант № 2. Почувствуй себя профи

Более гибкое и идеологически правильное решение не имеет недостатков, присущих предыдущему методу.

Вариант основан на бэнд-технологии; автор будет исходить из того, что читатель знаком с механизмом использования бэндов. Если же для Вас это что-то новое — обратитесь к документации по FastReport («Построение отчетов → Используем бэнды» и далее по тексту) там все очень подробно и доступно описано.

Рассматривать реализацию будем на том же примере.

Как известно, дата-бэнды выводятся для каждой записи в базе данных (таблице). Таблицей в данном случае называют «заранее неизвестное количество строк (записей), каждая из которых содержит определенное количество колонок (полей)» (© FastReport 4.0 Руководство пользователя). Как видите, это именно наш случай. Осталось только найти базу данных.

Вот так, плавно мы и подошли к одной из «фишек» Геопроекта, которая выделяет его среди других ГИС. Имя этой фичи — Виртуальная база данных (VBD). Виртуальной она названа не просто так: на самом деле это всего лишь счетчик записей и никаких данных в этой БД не хранится. Перед использованием Виртуальную базу необходимо инициализировать — указать количество записей, которое она будет «хранить». Делается это при помощи процедуры

VBDCount(Count);

где Count — целое число, определяющее количество записей.

Все действия по обработке и выводу информации отданы на откуп пользователю. Работает это примерно так:

  • каким-либо образом собираются необходимые данные;
  • задается количество «записей» в базе;
  • в отчет добавляется дата-бэнд;
  • в качестве источника данных для бэнда указываем Виртуальную БД;
  • при построении отчета бэнд пропечатывается указанное количество раз, при этом пользователь сам должен обеспечить вывод необходимой информации.

Как видим, разработчики сделали все, что можно, дабы облегчить нам жизнь и даже чуть-чуть сверх того.

Начинаем создавать таблицу: на страницу кладем бэнд «Заголовок данных» и размещаем в нем шапку таблицы. Под заголовком располагаем дата-бэнд «Данные 1 уровня»; двойным кликом по его заголовку вызываем окно «Источник данных», выбираем пункт «Виртуальная БД» и жмем OK.

Из текстовых блоков составляем одну строку таблицы. В ячейку с порядковым номером вписываем текст:

[Line#]

Эта служебная переменная выводит номер текущей записи в базе данных. Осталось установить количество записей в Виртуальной БД, это делается при помощи процедуры VBDCount:

begin
  VBDCount(SelectCount);
end.

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

Теперь нужно сделать так, чтобы таблица заполнялась необходимой информацией. Сделать это, как и многое другое, можно несколькими способами:

  1. заранее сформировать массивы необходимой размерности и выводить уже значения элементов массива;
  2. формировать значение непосредственно перед выводом в ячейку;
  3. вставить вызов функции, возвращающей семантику, прямо в ячейку.

Однозначно сказать, какой способ лучше, нельзя — задачи бывают самые разные. Если нужно только сформировать таблицу и значения состоят из одной строки семантики, оптимальным будет последний метод. Для случаев, когда необходима предварительная обработка значений, идеальны первый и второй вариант. Если же данные потребуются еще и для других целей, то игнорировать массивы (первый вариант), на мой взгляд, просто неразумно. Естественно, можно комбинировать эти методы.

Итак, Вы решили вставить в ячейки вызов функции Semantic с необходимыми параметрами. Но сначала нужно сделать небольшую настройку ячеек нашей таблицы: необходимо задать другие символы для обозначения выражений. Выделяем все ячейки в дата-бэнде и в Инспекторе объектов меняем свойство ExpressionDelimiters с «[,]» на что-то другое, например «<,>». Это нужно для того, чтобы ядро FastReport могло корректно обработать выражения. Теперь в ячейки можно вписывать примерно такую конструкцию:

<Semantic(FindCode(<Line#>), '0014', 1)>

При построении отчета вместо <Line#> будут подставляться порядковые номера объектов, и в итоге таблица окажется заполненной необходимыми значениями.

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

<OwnerName[<Line#>]>

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

Ну и наконец, последний способ заполнения ячеек: для каждого текстового блока создаем событие OnBeforePrint в котором и происходит присвоение нужного значения свойству Text.

procedure MemoOwnerOnBeforePrint(Sender: TfrxComponent);
begin
  MemoOwner.Text:=Semantic(FindCode(<Line#>),'0014',1)
end;

Не часто, но встречаются ситуации, когда использовать описанные способы создания таблиц не представляется возможным. Но вдруг Вы именно такой, редкий. Тогда у вас один выход — полностью программное создание таблицы на этапе формирования документа. Единого рецепта здесь нет, практически все зависит от самой таблицы и требований к результату. Отличным примером такого подхода является шаблон «Каталог границ», созданный Sickle (шаблон можно скачать на форуме поддержки). Желающие создавать таблицы «на лету» разберутся с ним без труда.

Tips & tricks

Напоследок позволю себе дать несколько советов:

  • включите выравнивание объектов по сетке (меню «Вид → Настройки», поставить птицу «Выравнивать по сетке»);
  • используйте направляющие, с их помощью гораздо легче создавать таблицы;
  • не используйте в качестве ячеек таблицы объект RichText, иначе объем шаблона сильно увеличится. К тому же особых преимуществ перед обычным текстовым блоком (Memo) у него нет;
  • готовую таблицу стоит сгруппировать, это нехитрое действие сэкономит массу времени и нервов, когда потребуется переместить таблицу в другое место листа или перенести ее в другой шаблон;
  • длинная таблица будет гораздо лучше читаться, если на каждой новой странице проставить заголовки столбцов и сделать выделение строк через одну.

Вот и все, что я хотел рассказать про таблицы. Успехов в освоении ГИС Геопроект!

Tестовый проект для сценария DZK-Script и два шаблона, реализующие описанные методы создания таблиц, можно скачать здесь.

Мітки:

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

*