Одной из наиболее распространенных графических систем в среде ОС Unix является система X-Window [1]. Разработка прикладных систем на ее базе возможна с использованием средств различного уровня абстракции - от низкоуровневой X-Lib до специализированных инструментальных пакетов [2, 3]. Один из них - пакет Qt Toolkit, разработанный норвежской фирмой Tech Troll (ftp.troll.no) в 1996 г. (версия 1.1 - декабрь 1996 г.). Qt представляет собой многоплатформную аппаратно-независимую библиотеку классов для организации графического интерфейса и существует в версиях для X-Window и Windows NT.
В библиотеке Qt реализовано полное покрытие базовой оконной подсистемы с созданием абстракции, не зависящей от текущей платформы. Благодаря конвертированию всех внешних событий во внутренний формат программа, реализованная для одной платформы, переносится на другую без изменений (естественно, при условии независимости от платформы функциональной части программы). Библиотека свободно распространяется для некоммерческого использования.
С точки зрения проектирования методология, предлагаемая Qt, отличается от общепринятой в библиотеках подобного назначения: для организации взаимодействия между объектами в ней служит не механизм событий, как в библиотеках классов OWL (Borland) и MFCL (Microsoft), а механизм сигналов.
Использование
В ООП известно три основных метода организации взаимодействия объектов (экземпляров класса): использование, механизм событий и механизм сигналов. Язык, реализующий парадигмы ООП, должен содержать по крайней мере один из них, а может содержать и все три. В Си++ реализовано только использование, когда в класс A включается поле, представляющее собой объект или указатель на объект класса B (см. листинг 1).Данный метод имеет существенные недостатки, в частности:
- объект класса A может быть "соединен" только с объектом класса B или производным от него;
- отсутствует возможность "широковещательной" операции, когда требуется передать информацию нескольким присоединенным объектам (такую передачу можно реализовать с помощью списков или аналогичных конструкций для хранения сведений о "соседях").
Сообщения
Механизм сообщений представляет собой значительный шаг вперед. Он и не реализован в самом языке Си++, но его можно имитировать, что и сделано в некоторых библиотеках классов, в частности, библиотеках для построения пользовательских интерфейсов в среде Windows, таких как OWL фирмы Borland. Суть механизма сообщений заключается во введении специального класса "Событие" (Event) и включение в базовый класс всех основных объектов виртуального метода "Обработка события" (HandleEvent; см. листинг 2).Класс "Событие" может хранить информацию о получателе, отправителе и т. д. Виртуальный метод "Обработка события" передает пришедшее событие всем своим видимым соседям до тех пор, пока кто-то его не обработает и не вернет, предположим, логическое значение TRUE. Если все соседи вернули FALSE, то объект должен сам проанализировать это событие и решить, обрабатывать его или нет. Правила, по которым происходит рассылка события, устанавливаются программистом при реализации метода обработки. Достоинствами этого способа организации взаимодействия являются:
- наличие унифицированного протокола взаимодействия между объектами;
- большая свобода в выборе классов, участвующих в организации информационной сети, чем в случае использования объектов;
- возможность расширения структуры сообщения путем создания производных классов от класса "Событие".
- сложная структура класса "Событие" и большой список допустимых событий затрудняют изучение класса;
- при введении "своих" типов событий разработчику необходимо заботиться об отсутствии конфликтов с уже существующими;
- при перекрытии правил обработки событий необходимо помнить о действиях "по умолчанию" для данного сообщения;
- часто возникает необходимость в использовании первого подхода для связи между объектами ввиду ограничений на маршрутизацию событий, накладываемых базовыми классами библиотеки;
- обработчик событий превращается в огромный case-оператор, усложняющий его восприятие.
Библиотека Qt и сигналы
Библиотека классов Qt предлагает расширение языка Си++, в котором становится возможным использование всех трех методов коммуникации. Механизм сигналов вводится в язык с помощью новых понятий "сигнал" (не путать с системными сигналами ОС Unix) и "слот". Сигналы - это метод представления информации в виде вектора. Сигнал может обладать параметрами, задаваемыми при его возбуждении. Слот - это особый тип метода объекта, предназначенный для обработки сигнала.Данное расширение сказывается на синтаксисе языка (см. листинг 3): к традиционным секциям описания класса - public, private и protected - добавляются новые. В листинге это public slots и signals; могут существовать также аналогичные описания методов, но тип возвращаемого значения обязательно должен быть void. Других ограничений не накладывается. Правила видимости - те же, что для обычных методов.
Слоты должны быть реализованы как обычные методы и в принципе почти ничем от них не отличаются: их можно точно так же вызывать, они наследуются, могут быть виртуальными (сильный механизм). Но главное назначение слота - это обработка сигнала. Сигнал представляет собой информационную посылку от одного объекта другому. Информационная сеть строится путем соединения сигналов одного объекта со слотами другого. В листинге 4 приводится пример возможной реализации классов из листинга 3.
Как видно из примера, возбуждение сигнала производится с помощью оператора emit <имя сигнала>(<аргументы>), а соединение сигнала и слота - с помощью функции connect (см. функцию main()). Соединяться могут только однотипные слоты и сигналы, т. е. такие, у которых совпадают тип и порядок следования параметров. Результат работы программы легко предсказуем.
Необходимо отметить, что один сигнал может быть соединен с несколькими слотами, а один слот с несколькими сигналами. Кроме того, соединение не является жестким механизмом, так что, например, удаление объекта "a" с последующим вызовом b->RuleB() не приведет к краху программы: просто внутренний диспетчер выведет сообщение о том, что сигнал не обработан, но даже и выдачу такого сообщения можно отключить.
Поддержка механизма сигналов
Расширение языка путем введения дополнительных операторов неизбежно должно вести к изменению транслятора, но гибкость Си++ позволяет без этого обойтись. Все вышеперечисленные элементы реализуются за счет использования директив препроцессора языка Си++ и применения специального макрогенератора, входящего в состав библиотеки. Макрогенератор moc предназначен для создания скрытых модулей поддержки сигнального механизма на основе информации, хранящейся в заголовочных файлах. Например, если определения классов из листинга 3 находятся в файле probe.h, запуск moc производится следующим образом: moc probe.h m_probe.CВ результате работы moc создается файл m_probe.C, содержащий все необходимые функции для поддержки сигнального механизма (ведь в базовом языке его нет).
Использование событий в Qt также возможно, однако применение сигналов позволяет решить ряд задач:
- построить логичную и простую информационную сеть объектов;
- упростить проектирование взаимодействия объектов;
- организовать взаимодействие разнородных объектов;
- облегчить проектирование программы за счет удобного механизма абстрагирования связей в реальном мире;
- значительно сократить объем сведений, необходимый для использования некоторого класса.
Библиотека классов Qt была использована авторами при разработке интерактивной системы математических расчетов BMC (Big Mathematical Calculator; в настоящее время она распространяется по GNU Public License и может быть получена с ftp-серверов ftp.nordlink.ru/pub/unix/X/bmc или ftp.isis.org.ru/pub/unix.bmc). Применение сигнального механизма коммуникации позволило значительно сократить время проектирования структуры классов, а упрощение самой этой структуры существенно уменьшило затраты усилий на тестирование модулей пользовательского интерфейса.
Листинг 1. Использование объекта
class B { public: void RuleB(); } class A { public: B *b; void RuleA(); }Листинг 2. Механизм событий
class Event { int Code; } class Base { public: virtual int Handle(Event& E); } class A: public Base { public: virtual int Handle(Event& E); }Листинг 3. Механизм сигналов
class A: public QObject { Q_OBJECT public: void RuleA(); public slots: void RouteA(int); signals: void SigA(double); } class B: public QObject { Q_OBJECT public: void RuleB(); public slots: void RouteB(double); signals: void SigB(int); }Листинг 4. Пример обработки сигналов.
// A ////////////////////////////////////////// void A::RuleA() { printf("Work RuleA "); } void A::RouteA(int I) { RuleA(); printf("Work RouteA(%d) ",I); emit SigA(3.0); } // B ////////////////////////////////////////// void B::RuleB() { printf("Work RuleB "); RouteB(1.0); emit SigB(2); } void B::RouteB(double X) { printf("Work RouteB(%lf) ",X); }; // main ////////////////////////////////////// void main() { A *a=new A; B *b=new B; connect(a,SIGNAL(SigA(double)),b,SLOT(RouteB(double))); connect(b,SIGNAL(SigB(int)),a,SLOT(RouteA(int))); b->RuleB(); delete a; delete b; }
Литература
- Gettys J., Scheifler R. X-Lib. - C language X-Window interface MIT X consortium standard version 11, release 5.
- Heller D., Ferguson P. Motif Programming Manual.
- McCormack J., Asente P., Swick R. X Toolkit Intrinsics. - C language interface X-Window system version 11, release 5.
Евгения и Михаил Фрейдеры - выпускники Челябинского государственного технического университета, работают в исследовательской лаборатории по вычислительной технике в университете шт. Нью-Мексико. E-Mail: jfreider@crl.nmsu.edu