Играем в Начинающие программисты на языке одинСи часто задают вопрос: "Как работать с объектом "Таблица" в 1С?". В принципе данная тема достаточно хорошо раскрыта в ЖКК, и поэтому частота появление этого вопроса вызывает недоумение, во всяком случае, у меня. Тема сама по себе большая, и ее трудно уместить в рамках одной статьи. По этому остановимся на самом популярном вопросе: "Как раскрасить таблицу в 1С?". Самым лучшим способом разобраться, как работает не понятный тебе объект, это написать программу, которая работает с данным объектом. Вот и мы сейчас попробуем реализовать что-нибудь, используя агрегатный объект "Таблица". Если быть точнее, то мы с вами сейчас реализуем игру "Жизнь", придуманную в 1970 году математиком Дж. Конвеем. Почему игру, и почему именно "Жизнь". Во-первых, разбираться как кодиться игра все-таки интереснее, чем смотреть на сухой код вывода отчета на печать. Во-вторых, код ее достаточно прост и не будет загромождать суть разбираемого нами вопроса, то есть "раскрашивание" объекта "Таблица". Итак, начнем играть. | | Автор статьи: Волшебник | Редакторы: Последняя редакция №3 от 09.01.06 | История URL: http://kb.mista.ru/article.php?id=67 | |
Правила игры
Действие игры происходит на некой плоскости, разделенной на клетки. Каждая клетка плоскости может принимать два состояния. Есть на ней жизнь или нет. На состояние любой клетки оказывает влияние состояние соседних клеток. С определенной дискретностью на плоскости происходит смена поколений. Состояние любой клетки под воздействием "Генетического закона Конвея" может измениться. Говоря по-русски, жизнь в любой момент может прекратиться либо наоборот появиться на свет. Законы развития просты как три копейки, точнее две, и состоят из двух пунктов:
1. Рождение. На любой не занятой клетки может возникнуть жизнь, если в окрестности Мура, восемь соседних клетках, есть ровно три живых организма.
2. Смерть. Любой живой организм вымирает от недостатка питания или общения, по этому, если в окрестности Мура менее чем два соседа или более трех, организм умирает.
Далее интерпретация правил несколько варьируется.
Первое различие касается плоскости, или мира. Мир может быть замкнутым, а может быть ограничен. В ограниченном мире у любой угловой клетки в окрестности только три соседа, а у любой боковой, но не угловой, их пять. В замкнутом мире у любой клетки соседей всегда восемь.
Второе различие. Когда игра считается законченной. Тут много разных вариантов, и цикличность поколений, переход в колебательное или устойчивое состояние. Все это нам не надо. Ведь мы на самом деле играть не собираемся, не так ли. Мы оставим один вариант конца игры - это вымирание колонии.
Теперь не много перелопатим правила под свои нужды. Игра будет происходить на плоскости 40х17 замкнутого пространства. Играющий может влиять только на расстановку живых сил первого поколения. Иначе говоря, игрок сам создает колонию. Колония может быть создана случайно, с заданием плотности населения. Либо руками, то есть когда бог сам говорит: "Да будет жизнь". После смены поколения изменить что-либо в колонии будет нельзя. Смена поколений происходит путем применения генетического закона Конвея. Игра заканчивается смертью колонии, как от естественных причин, так и от "милости" бога, путем всемирного потопа или иного Армагеддона.
Ну, с правилами разобрались, приступаем к дизайну.
Дизайн
Позвольте не много уклониться от темы и заметить, что многие программисту отбрасывают такую вещь как дизайн приложения напрочь. Это очень не мало важный фактор. Конечный пользователь вряд ли будет любоваться оригинальностью вашего кода. Просто по одной причине, он ни черта не поймет. А вот красявость вашей аппликации может стимулировать как интерес, так и наоборот, полное отрицание. Поэтому уделяйте дизайну приложения больше сил и возможностей.
Сначала глянем на форму. Почесав затылок, приходит к выводу. Тут дизайн на фих не нужен. Ведь мы будем работать с объектом "Таблица" и форма будет скрыта за ней. Поэтому ляпаем на форму, что-то вроде эбоута. И едем дальше, к таблице.
Тут уж есть, где разгуляться. И все будет зависеть от буйности вашей фантазии. Я буйствовать, сильно не буду. Нету на это время.
Запускаем пофигуратор и переключаемся на объект "Таблица". Для начала выставляем у 42 клеток ширину равную 3 у.е. в системе 1С. И у 23 строк выставляем высоту строки равной 15 единицам. После чего у двух строк, а именно второй и четвертой выставляем высоту строки 8,25. У строки 4 делаем высоту равной 20,25. Выделяем область таблицы с координатами верхней левой [R1C1] и нижней правой [R23C42], в дальнейшем области будем показывать так [R1C1:R23C42] и установим для ячеек этой области следующий формат используемого шрифта (см. рисунок №1).
Рис. 1
Далее, для области [R2C2:R22C41] используя свойства ячеек, сделаем жирную рамку (см. рисунок №2).
Рис.2
Теперь займемся раскраской. Для ячеек областей таблицы [R2C2:R4C41] и [R21C2:R21C41] установим в качестве фона светло-серый цвет (см. рисунок №3).
Рис.3
Теперь у нас получилась прямоугольная область с четко обозначенными границами живого пространства. Теперь немножко подумаем, как отделять нам клетки друг от друга. Можно конечно положиться на саму 1С, сказав ей выводить границы ячеек. Но мне кажется это не совсем красяво. Ведь тогда границы будут и там, где они сто лет не нунжны, а это не есть гуд. Выделяем область [R5C2:R21C41] и для ее ячеек устанавливаем следующие свойства (cм. рисунок №4)
Рис.4
Правда, после этого у нас стали не совсем кошерно отображаться границы справа и слева. Исправляем косяк. Для этого у области [R5C2:R21C2] выставляем свойства похожие на те, что показаны на рисунке №2, с одним различием. Будем не обводить, а рисовать только слева. Нам ведь только там нужна жирная граница. Для этого в группе "Рамка" вместо "Обвести" выбираем "Лево". Аналогично поступаем с областью [R5C41:R21C41], с той лишь разницей, что выставляем границу справа.
Надеюсь, понятно для чего я выкрасил верхнюю и нижнюю часть в светло-серый цвет. Да. Вверху будет у нас панель управления, внизу строка состояния.
Займемся панелью управления игрой. Для управления игрой нам понадобиться следующее кнопки:
- запуск игры,
- смена поколения,
- Армагеддон,
- команда генерации нового поколения,
- и как необязательная опция кнопки для чтения и сохранения текущего состояния игры.
Путей тут много, я пошел одним из самых простых. Выделяем в нашей таблице следующие области: [R3C3:R3C7], [R3C9:R3C13], [R3C16:R3C20], [R3C22:R3C26], [R3C28:R3C32] и [R3C35:R3C39]. И делаем объединение ячеек. После чего, в цикле, у каждой из шести ячеек выставляем следующие свойства (см. рисунок №5)
Рис.5
У всех шести ячеек свойства совпадают за не большим различием. В том месте, где у меня написано Load, для каждой из ячеек надо написать свое значение (я использовал следующие наименования: "Load", "Save", "Begin", "Next", "Random", "Finish"). Так же вам надо обратить на поле "Расшифровка". В нем обязательно должно быть написано слово "Hook". Для чего, объясню чуть позже, когда займемся кодированием игры.
Теперь займемся строкой состояния. В ней нам надо будет выводить только два значения: количество живых существ и номер текущего поколения. Границ нам тут не надо. Единственное что я сделал (см. рисунок №5, пункт №2), правда, с небольшим отличием установил выравнивание по правому краю. Итак, поехали. Объединяем в таблице следующие области: [R22C3:R22C6], [R22C7:R22C10], [R22C12:R22C16] и [R22C17:R22C20]. В них пишем, по порядку, следующее: "Count life:", "0", "Period:", "0".
Наконец наведем несколько приятных штрихов. Для области [R5C2:R5C41] установим границу сверху, черным цветом, стилем непрерывная линия толщиной как у кнопок (см. рисунок №5, пункт №3). Аналогично, только снизу, проводим границу для области[R21C2:R21C41]. И на конец у ячеек [R22C11] и [R22C21] проводим такую же границу справа.
Вообще границы у объекта "Таблица" вещь рульная. Играя ими, можно добиться кучу поразительных визуальных эффектов. Например, такой:
Рис.6
С дизайном покончено. Прежде чем окончательно закрывать таблицу нам надо сделать две вещи. Первая это сформировать секции, в принципе не обязательно, но для избегания всяких несуразностей лучше сделать. Я сформировал две. Одну вертикальную, назвав ее "Width". Другую горизонтальную - "Height". Вторая вещь долгая и нудная. Как тут пошустрее выкрутиться описывать не буду. Вам надо, для всех ячеек области [R5C2:R21C41] в поле "Расшифровка" написать слово "Hook".
Если вы сделали все правильно, то у вас должно быть получиться, нечто, похожее на это:
Рис.7
Он сказал: "Поехали", и махнул рукой
Для начала, на всякий случай, позвольте вам напомнить структуру модуля отчета. То есть что куда и зачем писать.
1. Блок описания глобальных переменных. То есть переменные, которые видны и доступны все модулю.
2. Блок процедур и функций. Тут размещаются все процедуры и функции нашего модуля. Каких либо строгих правил нет. Кроме одного: все процедуры и функции, используемые другими процедурами или функциями, должны располагаться сверху от вызывающих их процедур или функций.
3. Блок инициализации модуля. Здесь выполняются действия, которые необходимо выполнить в самом начале работы модуля, до того как форма обработки выведена на экран. В принципе данный блок можно опустить, используя вместо него переопределенную функцию OnOpen. Кому как удобнее.
Надеюсь вывод табличной части на экран, не вызывает не у кого усиленное сердцебиение. Поэтому останавливаться на этом подробно не будем. А подумаем над такой вещью. Как сделать так, что бы пользователь увидал нашу таблицу поверх формы. Правда, зачем пользователю смотреть на нее, все равно управление игрой будет происходить в таблице. Сразу напрашивается вариант с процедурой OnOpen. Но он не совсем подходит, так как форма выскакивает поверх таблицы, и для переключения на таблицу надо будет делать лишни телодвижения. Вариант с функцией на форме, тоже не прокатывает. Почему, спросите у 1С. Что делать. Делать не фих, снимаем трусы и начинаем бегать:
var Mode;
var true, false;
var Table;
procedure OnClose()
if Mode = true then
Mode = false;
Table = createobject("table");
Table.SourceTable(Table);
Table.PutSection("Height|Width");
Table.Protection(true);
Table.Show("game ""Life""");
returnstatus(false);
endif;
endprocedure
procedure OnOpen()
true = 1;
false = 0;
Mode = true;
Form.Close();
endprocedure
Теперь глянем, что мы тут навыбегали. У нас тут объявлены две предопределенные процедуры. Одна из них вызывается при открытии, другая при закрытии формы. Суть всей фичи такова. В процедуре при открытии формы, пока она еще не отобразилась на экране, мы закрываем форму, предварительно выставив флаг Mode в положение true. Он нам нужен для того, что бы процедура OnClose знала, кто ее пытается закрыть, процедура OnOpen или нечто другое. Поскольку форма закрывается, наступает очередь процедуры OnClose. Та смотрит на флаг, и если он выставлен в true, начинает рисовать таблицу, предварительно опустив Mode в положение false. И как только таблица нарисована, с помощью процедуры returnstatus обламывает закрытие формы. В результате трюка Таблица рисуется поверх формы, что нам и требовалось получить.
У вас могут возникнуть вопросы, зачем я использовал true, false и Table, да к тому же вынес их в блок глобальных переменных. Глобально я их объявил, потому, как они будут нужны куче других процедур и функций. А использование true & false могу только объяснить наглядностью. Во всяком случае, мне кажется так понятнее, чем просто 1 и 0, как учит нас 1С.
Следующее, что бросается в глаза, так это отсутствие функции Random в языке 1С. А ведь нам она понадобиться. По условию задачи, жизнь может генерироваться случайно. И тут на помощь приходит avb он же Алексей Бажитов, надо заметить, что и трюк с выводом таблицы я узнал от него. И так функция Random:
// объявляем глобальную переменную
var GlSeed;
function Random(MaxValue)
GlSeed = GlSeed * 1103515245 + 12345;
return ((GlSeed / 65536) % 32768) % MaxValue + 1;
endfunction
// добавляем в процедуру OnOpen
GlSeed = _getperformancecounter();
Объяснить тут мало, что можно. Стоит только сказать, что данный алгоритм был предложен, комитетом ANSI-C, для тех, кто не знает, он вырабатывает стандарты для языка Си. В том, что добавляется в процедуру OnOpen, используется не документированная функция языка 1С, которая возвращает число миллисекунд прошедших с момента включения компьютера. И в данном случае выполняет функцию, аналогичную паскалевской или бейсиковой функции randomize. Сама функция Random возвращает случайно число из диапазона от 1 до MaxValue. Все теперь рандом у нас есть, и поверьте мне не плохой. Из всех виденных мною вариантов, самый лучший, для одинЭс конечно.
Далее нам нужен двухмерный массив размером 40х17. Не спешите хлопать в ладоши, говоря, что у 1С нет двухмерных массивов. Спорим есть. Надо просто внимательно глянуть на агрегатный объект, слово то придумали в фирме 1С, "ТаблицаЗначений". Конечно, у него есть не которые минусы по сравнению с обычным массивом, но зато есть и куча плюсов. Останавливаться на них мы не будем, а просто объявим глобальную переменную и создадим для нее пустой агрегатный объект "ТаблицаЗначений", имеющий 40 колонок и 17 строк.
var tabTemp;
tabTemp = createobject("valuetable");
for x = 1 to 40 do
tabTemp.NewColumn("K" + string(x), "number", 1, 0);
enddo;
for x = 1 to 17 do
tabTemp.NewLine();
enddo;
Так еще, что первое бросается в глаза, из глобальных переменных, нам нужны две, в которых будем хранить количество жизней в текущем поколении и соответственно номер самого поколения. Объявляем их:
var Life, Period;
//и инициализируем:
Life = 0;
Period = 0;
Вроде все готово к штурму замка. Сохраняем обработку. Запускаем интерпретатор. Отрываем обработку там. Любуемся (см. рисунок №8).
Рис.8
Вроде все так, но что-то не так. Зачем нам в самом начале нужны активные кнопки "Save", "Begin", "Next" и "Finish". Правильно, все они нужны в тот момент, когда игра уже началась. Да и то не всегда. Вот тут-то ребята мы и подошли к первой части нашего вопроса о раскраске таблиц. Научимся менять цвет текста в таблице. Для изменения цвета текста ячейки таблицы служит функция для работы с агрегатным объектом "ОбластьТаблицы" TextColor. Данная функция может использоваться как для изменения цвета текста области таблицы, так и для получения информации о цвете текста у заданной области. Все это понятно, но что за новый объект "ОбластьТаблицы". В ЖКК про него ни гугу, и как нам его получить. И тут вы не правы. ЖКК упоминает о нем, конечно не так подробно как о "Таблице", но на безрыбье и рак рыба. А если положить лапу на сердце то можно утверждать, что в данной книжке достаточно материала об агрегатном типе данных "ОбластьТаблицы". Для того, что бы получить объект "ОбластьТаблицы" нам нужно использовать метод Area объекта "Таблица". Пока неясно, ни чего страшного, далее все будет понятнее. Продолжаем кодить игру. Итак, у нас есть пять кнопок, активностью которых мы хотим управлять. Если текст кнопки черный, значит кнопка - активна. Если же цвет текста темно-серый, значит кнопочка энейбл. Для этого объявим пять глобальных переменных:
var flLoad, flSave, flBegin, flNext, flRandom, flFinish;
//установим им значения на начало игры:
flLoad = true;
flSave = false;
flBegin = false;
flNext = false;
flRandom = true;
flFinish = false;
а дальше делаем процедуру, которая будет менять цвет текста в зависимости от выставленных флагов:
procedure RefreshTable()
Table.Area("R3C9").TextColor(?(flSave = true, 0, 8421504));
Table.Area("R3C16").TextColor(?(flBegin = true, 0, 8421504));
Table.Area("R3C22").TextColor(?(flNext = true, 0, 8421504));
Table.Area("R3C28").TextColor(?(flRandom = true, 0, 8421504));
Table.Area("R3C36").TextColor(?(flFinish = true, 0, 8421504));
endprocedure
Было бы неплохо заодно сообщать пользователю, о количестве живых существ в нашем мире. Да заодно и номер поколения. Для этого у уже известного нам объекта "ОбластьТаблицы" есть свойство, не путать с методом, Text. Данное свойство доступно как для чтения, так и для записи. Добавляем в нашу процедуру следующие строчки:
Table.Area("R22C7").Text = string(Life);
Table.Area("R22C17").Text = string(Period);
А теперь один нюанс. Для того чтобы пользователь увидел сделанные в таблице изменения, таблицу надо перерисовать, используя уже известный нам метод Show. Добавляем следующую строчку:
Table.Show();
Ну и теперь добавим вызов вновь созданной процедуры сразу же после первого вывода таблицы на экран, в процедуру OnOpen, следом за строкой "Table.Show("game ""Life""");". Сохраним изменения и полюбуемся на результат:
Рис.9
Вот нами и сделан первый шаг, на пути полной победы коммунизма в отдельно взятом мире. Как говориться, за что боролись, на то и напоролись ;))
|