Есть колеса,
нету гривы,
нет
на хвост волосиков.
В.Маяковский. Конь-огонь.
В этой статье мы, как и в предыдущих* разовьем с помощью техники конечных автоматов типовой пример программы из комплекта Visual C++. На сей раз речь пойдет об управляющих элементах ActiveX.
Но если создать такой модуль совсем легко, то "оживить" намно о сложнее. А иногда необходимо (или просто очень хочется), чтобы объект не только красиво выглядел, но и "жил собственной жизнью". Тот, кто знаком с ассистентами Microsoft Office 97 (Word, Excel и т. д.) или переводчиком Magic Gooddy фирмы ПРОМТ, без труда поймет, о чем идет речь.
Чтобы наделить объект "жизнью", необходима модель, определяющая его поведение во времени. В этой роли выступит конечный автомат, а создать объекты ActiveX с автоматными свойствами нам, как и в предыдущих статьях, поможет библиотека FSA. Ее можно получить по адресу http://www.osp.ru/pcworld/2000/02/spfire.zip.
Создание просто о элемента ActiveX
Проектирование
Первым делом создадим элемент ActiveX, стараясь как можно меньше вмешиваться в работу Visual C++.
- В меню File выберем пункт New, а в открывшемся окне - закладку Project.
- В поле Project Name введем имя проекта (например, Ellipse), а в поле Location - путь к папке, де он будет располагаться, после че о запустим мастера проектов, дважды щелкнув на значке MFC ActiveX Control Wizard.
- Нажмем кнопку Finish.
- Прочтем информацию о создаваемом проекте и нажмем кнопку OK. Мастер завершит работу.
- Скомпилируем и скомпонуем элемент, нажав клавишу или выбрав команду Build Ellipse.ocx в меню Build.
Так, "одной левой", мы создали "фирменный" элемент ActiveX. Но мы еще не знаем, как он выглядит и как работает. Чтобы удовлетворить наше любопытство, элемент необходимо протестировать. Это чуть сложнее, чем создать его, но ненамного.
Тестирование
Посмотреть на элемент ActiveX можно либо прямо из среды Visual C++, либо встроив его в какой-нибудь документ.
В первом случае следует выбрать в меню Tools команду ActiveX Control Test Container, которая запускает специальную про рамму тестирования элементов на базе модели COM из Microsoft SDK. В меню Edit этой про раммы выберем пункт Insert OLE Control, после чего (возможно, с небольшой задержкой) на экране появится диалоговое окно с перечнем зарегистрированных на текущий момент в операционной среде элементов. Найдем в нем только что созданный элемент (его имя - EllipseCtl) и нажмем или кнопку OK.
После всех этих манипуляций мы наконец увидим наш элемент ActiveX, представляющий собой прямоу ольное окно с вписанным в не о эллипсом. Элемент можно будет немножко "помучить" - подвигать, изменить е о размеры и т. д., - а также размножить, для че о достаточно выбрать в меню появившийся там пункт OCX. На экране возникнет очередной эллипс в прямоу ольнике; только не забудьте предварительно сдвинуть ранее вставленный элемент, иначе новый его закроет.
Во втором случае нам понадобится документ, например, такой, как показан в Листинге 1. За основу в нем был взят текст из проекта с именем Poly, созданно о мастером ATL COM AppWizard (почему-то этот мастер создает файл с расширением htm, а MFC ActiveX Control Wizard - нет), в котором потребовалось только изменить значение параметра CLASSID на идентификатор нужно о нам класса. Первоначально в документе стояло:
CLASSID="CLSID: B1BFF0B2-5C58-11D3-9BDF -00E04CDD233A">
Идентификатор содержится в файле Ellipse.odl, который откроется при щелчке на пункте _DEllipseEvents в списке классов проекта, и находится в строках:
// Class information for CЕllipseCtrl [ uuid(B1BFF0B2-5C58-11D3-9BDF-00E04CDD233A), helpstring("Ellipse Control"), control]
Если дважды щелкнуть на созданном HTML-файле в Проводнике Windows, откроется окно Internet Explorer с тем же самым эллипсом.
И в первом, и во втором случае это скучный, "мертвый" эллипс. Есть ли способ как-нибудь "оживить" его, не очень при этом напрягаясь? Есть. Но прежде чем рассказать о необходимых для это о изменениях в проекте, обсудим новую "жизнь" эллипса, т. е. алгоритм, или, иначе, модель е о поведения.
Дышите... Не дышите...
Предположим, мы хотим, чтобы эллипс "дышал", изменяя свои размеры по оси X от некоторого заданно о значения до нуля и обратно. Пусть его максимальная ширина определяется размером по оси X окна, отображающе о элемент ActiveX (про рамма ActiveX Control Test Container очерчивает это окно прямоугольником).
Можно создать "дышащий" эллипс, внеся изменения в метод OnDraw, - например, так, как показано в Листинге 2 (строки, порожденные мастером, закомментированы). Однако тогда будет сложно организовать взаимодействие элемента ActiveX с приложением (в приведенном варианте проблемы будут связаны прежде всего с "вешающим" приложение циклом While).
Поэтому применим для решения задачи принципиально иной метод, взяв за основу не последовательную алгоритмическую модель, которая заложена в подавляющее большинство языков программирования (влючая и Си++), а автоматную, изначально рассчитанную на параллельную работу. Это снимет многие вопросы, в том числе и упомянутую опасность зависания.
Автоматная модель эллипса
Автоматная модель "дышащего" эллипса
Граф конечного автомата, реализующего"дышащий" эллипс |
Построим автоматную модель требуемого поведения эллипса. Граф соответствующего конечного автомата показан на рисунке, а его таблица переходов - в таблице. Все о у автомата три внутренних состояния: E0, E+ и E-.
В нашей модели два предиката (x1 и x2) и четыре действия (y1, y2, y3 и y4), которые можно условно описать, например, так:
// Предикаты x1 истина, если X <= nX; x2 истина, если X <= 0; // Действия y1 определить начальные размеры эллипса; y2 увеличить размер по оси X; y3 уменьшить размер по оси X; y4 отобразить эллипс;
(Здесь: X -- текущее значение радиуса эллипса по оси X, nX -- начальный размер эллипса по оси X.)
Функционирование модели
Автомат функционирует в дискретном автоматном времени, переходя из одно о внутренне о состояния в другое. В каждом такте он в соответствии с таблицей переходов анализирует актуальные для текуще о состояния предикаты и в зависимости от их значения выполняет тот или иной переход и сопутствующие ему действия. Начальное состояние модели (E0) выделено на рафе жирным кружком, а в таблице переходов е о описывает первая строка.
Из состояния Е0 автомат все да переходит в состояние Е+ и при этом выполняет начальное действие y1 - определяет ширину окна отображения и разрешает рисование объекта. В состоянии Е+ он наращивает длину оризонтальной полуоси эллипса до максимума (половина ширины окна), в состоянии E- уменьшает до нуля. При максимальном значении длины автомат переходит из состояния Е+ в состояние Е-, при нулевом - из E- в E+.
Запро раммировать такой автомат с помощью библиотеки FSA достаточно просто. В следующих разделах мы покажем, как это сделать.
Настройка проекта на FSA-среду
Прежде, чем приступить к изменению класса CElipseCtrl, отвечающе о за "жизнь" элемента ActiveX, необходимо произвести стандартную настройку проекта и внести изменения в некоторые файлы, общие для всех подобных проектов.
"Прошивка" путей к файлам за оловкам библиотеки FSA
Предположим, что файлы библиотеки FSA находятся в каталоге с именем LW_MC50NN. Чтобы установить с ними связь, выполним следующие шаги:
1. В поле настроек проекта Additional include directories (Project*Settings...+*закладка С/С++: Preprocessor) внесем строку:
lw_mc50nninclude
2. В окне списка файлов проекта выберем закладку Fil..., затем папку Header files, откроем в ней файл StdAfx.h и вставим в него строку:
#include // FSA support for Finit State Automation Processes
3. Добавим к файлам наше о проекта FSA-библиотеку, чтобы разрешить внешние ссылки на нее. Для это о перейдем на закладку Fil..., щелкнем на лавной папке правой кнопкой мыши, выберем в контекстном меню пункт Add Files to Project, установим тип файлов .lib и укажем нужный путь. Файл библиотеки называется fsam532.lib и находится в катало е lw_mc50nn/source/fsa/release.
Создание автоматной среды
Для создания среды функционирования про раммных автоматов необходимо выполнить два условия:
- определить место для класса CArrayNetFsa;
- создать механизм периодическо о запуска метода OnIdleFsa, принадлежаще о классу CArrayNetFsa.
Класс CArrayNetFsa содержит все необходимое для реализации параллельной работы множества про раммных автоматов (даже более того - множества автоматных сред). Он же обеспечит нам дискретное автоматное время, которое моделируется с помощью периодическо о вызова метода OnIdleFsa.
Удобнее все о сделать CArrayNetFsa одним из родительских классов CEllipseApp. При этом файл заголовка примет следующий вид:
class CEllipseApp : public COleControlModule, public CArrayNetFsa { ... };
Метод OnIdleFsa хорошо было бы запускать, используя метод OnIdle, принадлежащий классу COleControlModule, а точнее, его предку CWinApp, но в случае ActiveX-объектов вызов это о метода перехватывается "на дальних подступах" средой (окном, документом), в которой находится объект. Поэтому реализуем дискретное время другим способом - подключим к приложению таймер, а в метод OnTimer вставим обращение к OnIdleFsa.
Таймер ле ко подключить к любому окну, т. е. классу, порожденному от CWin. В нашем проекте окно представляет собой класс с именем CEllipseCtrl, порожденный от COleControl, который, в свою очередь, порожден от нужного нам CWin.
Программируем ActiveX с автоматом
Прикладной класс элемента ActiveX представлен в проекте классом CEllipseCtrl. Чтобы придать ему автоматные свойства, нужно:
- включить в перечень е о классов-предков автоматный класс LFsaAppl из состава FSA-библиотеки;
- запро раммировать необходимые для функционирования автомата предикаты и действия;
- построить таблицу переходов автомата;
- подключить к приложению таймер.
В результате за оловок класса CEllipseCtrl примет вид, показанный в Листинге 3.
В "фирменный" за оловок были добавлены следующие переменные:
. bIfDraw -- признак разрешения рисования фи уры (для метода OnDraw);
. nX; -- максимальный размер эллипса по оси X;
. rcEllipse -- текущие размеры эллипса;
. x1, x2 -- предикаты автомата;
. y1, y2, y3, y4 -- действия автомата.
Для работы с таймером нам потребуются, кроме того, методы OnCreate и OnTimer (Листин 4), которые можно добавить с помощью AppWizard. Первый произведет при создании объекта инициализацию таймера, а второй будет обрабатывать поступающие от таймера сообщения. В конструкторе класса (Листин 5) нужно запретить рисование фигуры и подключить автомат к автоматной среде.
Необходимы также небольшие изменения в методе OnDraw класса CEllipseCtrl (Листинг 6). Во-первых, эллипс должен отображаться только при условии, что его рисование разрешено (т. е. когда bIfDraw=TRUE). Во-вторых, за размеры эллипса теперь отвечает не параметр rcBounds, а атрибут самого класса переменная rcEllipse.
Рассмотрим теперь компоненты, составляющие собственно автомат: таблицу переходов, предикаты и действия. Таблица переходов, задающая поведение автомата, показана в Листинге 7. Как видим, эта запись очень похожа на приведенную выше табличную форму. Сходным образом, реализация автоматных методов (Листинг 8) определяет на языке про раммирования то, что было изложено в свободной форме в перечне предикатов и действий модели эллипса.
Теперь все готово. Эллипс из простой статической картинки превратился в объект, живущий "полнокровной жизнью", которая определяется его автоматным поведением.
"Жить - хорошо, а хорошо жить..."
Итак, конечный автомат - простое средство придания динамики любому элементу ActiveX. Необходимые для этого условия понятны всякому, кто знаком с принципами построения и функционирования автоматной модели, а требуемые усилия невелики.
К минусам проекта следует отнести зависимость от таймера. Использование метода OnIdle сделало бы процедуру формирования дискретно о автоматно о времени более независимой и ускорило бы работу автоматной среды. Однако и с таймером можно добиться большей скорости работы, если, не выходя из обработчика, нужное число раз в цикле вызывать функцию OnIdleFsa.
Автоматная модель дополняет понятие объекта в ООП и COM, позволяя определить его поведение во времени, причем подходит как для единичных объектов, так и для множеств, и для сложных объектов, состоящих из нескольких параллельно работающих компонентов.
Задачу "оживления" типового элемента ActiveX мы решили. Теперь можно поработать над е о внешним видом, раскрасить, создать для не о новый алгоритм поведения - конечно же, автоматный.
Об авторе
Любченко Вячеслав Селиверстович. E-mail: slava@ivvson.kc.ru
Литература
1. Любченко В.С. О бильярде с Microsoft Visual C++ 5.0. "Мир ПК", 1998, N 1, с.202.
2. Любченко В.С. Батарея, огонь! или Задача Майхилла для Microsoft Visual C++. "Мир ПК", 2000, N 2, с. 148
* См. О бильярде с Microsoft Visual C++ 5.0. "Мир ПК", N 1/98, с.202; Батарея, огонь! или Задача Майхилла для Microsoft Visual C++. "Мир ПК", N 2/2000, с. 148.
Текущее состояние | Следующее состояние | Условие перехода | Действия |
E0 | E+ | - | y1 |
E+ | E+ | x1 | y2y4 |
E+ | E- | ^x1 | y3y4 |
E+ | E+ | ^x2 | y3y4 |
E+ | E- | x2 | y2y4 |