Next Previous Table of Contents

### 2. KDE и Qt Библиотеки

Норвежская компания Troll Tech ( http://www.troll.no) предоставляет так называемый GUI инструментарий (toolkit), именуемый Qt. GUI означает "Графический Пользовательский Интерфейс" ("Graphical User Interface"), поэтому базирующиеся на Qt приложения представляются кнопками, окнами и т.п. Такие приложения осуществляют взаимодействие с пользователем, представляя в визуальной форме выполняемые функции. Этот инструментарий необходим для разработки графических приложений, которые используют X-Window интерфейс в Unix системах, потому что X не содержит предопределенного пользовательского интерфейса. И хотя другие наборы инструментов также позволяют создавать пользовательский интерфейс, Qt предоставляет некоторые технические возможности, которые делают разработку приложений очень простой. Кроме того, Qt инструментарий также доступен для Microsoft Windows систем, что позволяет разработчикам выпускать приложения для обеих платформ.

Команда KDE ( http://www.kde.org) объединилась вместе с целью сделать Unix системы более дружественными для пользователя, и решила использовать набор инструментов Qt для разработки оконного менеджера для X-Window, плюс различных приложений, включенных в дистрибутив KDE. K Desktop Environment, таким образом, включает оконный менеджер kwm, менеджер файлов kfm и панель задач kpanel как главные компоненты, а также массу первоклассных утилит и приложений. После выхода KDE множество разработчиков обратили свои взоры к новой графической среде и к тому, что она предлагала. Библиотеки KDE предоставляют все необходимые методы и классы, которые делают процесс разработки приложений с их помощью однотипным и согласованным, и положение пользователя резко улучшается, так как он теперь должен разбираться только в специфике использования данного приложения, а не обучаться общим использования очередного варианта пользовательского интерфейса. Кроме того, KDE программы интегрируются в рабочую среду и предоставляют возможность взаимодействовать с менеджером файлов, используя drag'n drop, предоставляют возможность управления сеансами (session management) и многое другое, если разработчик реализовал все возможности, предоставляемые библиотеками KDE.

И Qt инструментарий, и библиотеки KDE реализованы с использованием языка программирования C++; поэтому большинство приложений, которые используют эти библиотеки, также написаны на C++. В последующих главах мы бегло ознакомимся с библиотеками, чтобы увидеть, что за инструмент нам предлагается и как вообще создаются Qt и KDE приложения.

### 2.1 Qt GUI инструментарий

Как уже говорилось, Qt библиотека - это инструментарий, который предоставляет графические элементы, используемые для создания GUI приложений, и который необходим при разработке программ для X-Window. Кроме того, Qt предоставляет:

- Широкий набор классов и методов, пригодных для использования даже для программ без графического интерфейса,
- Хорошее решение для реализации взаимодействия с пользователем посредством виртуальных методов и механизма сигнал/слот,
- Набором предопределенных GUI-элементов, называемых "widgets", которые могут быть легко использованы для создания видимых элементов,
- Дополнительные полностью предопределенные диалоговые окна, которые часто используются в приложениях, такие как окно прогресса и окно выбора файлов.

Знание Qt классов очень необходимо, даже если вы хотите разрабатывать только KDE-приложения. Чтобы получить первое представление о том, как GUI-приложения конструируются и компилируются, мы рассмотрим простую, основанную исключительно на использовании Qt программу; затем мы расширим ее до KDE-приложения.

#### Первое Qt приложение

Как правило, программа на C++ должна содержать функцию `main()`, которая является стартовой точкой для выполнения приложения. Так как мы хотим, чтобы наше приложение отображалось в графическом окне и предоставляло возможность взаимодействия с пользователем, первое, что мы должны знать, - как оно может показать себя пользователю. Посмотрим, например, на первую программу, включенную в учебник, поставляемый как часть Qt Online Reference Documentation, и объясним основные шаги выполнения. Итак, почему и как появляется окно приложения:

> `

>     #include <qapplication.h>
>     #include <qpushbutton.h>

>     int main( int argc, char **argv )
>     {
>     QApplication a( argc, argv );

>     QPushButton hello( "Hello world!" );
>     hello.resize( 100, 30 );

>     a.setMainWidget( &hello );
>     hello.show();
>     return a.exec();
>     }

> `

Это приложение просто рисует окно, содержащее кнопку с надписью "Hello world". Как и для всех основанных на Qt приложений, вы первым делом создаете экземпляр (instance) класса `QApplication`, представленный переменной `a`.

Затем программа создает экземпляр класса `QPushButton`, называемый `hello`, это будет кнопка. Конструктор `hello` получает строку в качестве параметра, которая представляет собой содержимое элемента, отображаемое как текст на кнопке.

Потом вызывается метод `resize()` кнопки `hello`. Это изменяет размер элемента по умолчанию (в данном случае QPushButton), который при создании имел длину 100 пикселей и высоту 30 пикселей. Наконец, метод `setMainWidget()` вызывается для объекта `a` и метод `show()` - для объекта `hello`. `QApplication` выполняет вызов `a.exec()`, входя в главный цикл событий (main event loop) и ожидая, когда он должен будет вернуть значение integer операционной системе, сигнализируя, что приложение завершилось.

#### Справочная документация для Qt

А сейчас мы кратко рассмотрим справочную документацию по Qt библиотеке. Для того, чтобы сделать это, запустите KDevelop и выберите "Qt-library" из "Help"-меню на панели меню. Средство просмотра документации откроет и покажет вам стартовую страницу справочного руководства Qt. Это первое место, где необходимо искать информацию о Qt, его классах и доступных функциях. Кроме того, приведенная выше программа входит первой в секцию "tutorials". Чтобы добраться к тем классам, которые мы хотим посмотреть, `QApplication` и `QPushButton`, выберите "Alphabetical Class List" и найдите соответствующие имена. Следуйте по любому из линков, чтобы посмотреть документацию по соответствующему классу.

Для `QApplication` вы увидите конструктор и все другие методы, которые есть в данном классе. Если вы нажмете на линк какого-либо метода, вы получите более детальную информацию об использовании этого метода, которая бывает очень полезна, если вы не можете определить, как правильно использовать метод, или хотите увидеть пример. Это также относится к документации по библиотекам KDE, которые используют похожую структуру; вот и почти все, что вы должны знать о просмотре документации по классам с помощью браузера.

#### Интерпретация примера

Просматривая `QApplication`, вы найдете все его методы, используемые в нашем примере:

- constructor `QApplication()`,
- `setMainWidget()` метод и
- `exec()` метод.

Пояснения, почему мы используем эти методы, очень простые:

1. вначале создаем экземпляр класса `QApplication` с помощью вызова конструктора, после этого мы можем использовать GUI элементы, предоставляемые Qt,
2. создаем элемент, который будет наполнением окна нашей программы,
3. устанавливаем этот элемент как главный элемент для `a`,
4. выполняем экземпляр `a` класса `QApplication`.

Второй объект нашей программы - кнопка (pushbutton), экземпляр класса `QPushButton`. Из двух конструкторов, предоставляемых для создания объекта, мы выбрали второй: он принимает в качестве аргумента текст, который будет отображаться как метка на кнопке; в данном случае это строка "Hello world!". Затем мы вызвали метод `resize()` для изменения размеров кнопки в соответствии с размерами ее содержимого - кнопка должна быть больше, чтобы весь текст был видимым.

А как насчет метода `show()`? Сейчас вы видите, что, как и большинство других элементов, `QPushButton` основывается на одиночном наследовании (single-inheritance). Документация говорит, что `QPushButton` унаследована от `QButton`. Перейдем по линку на описание класса `QButton`. Это откроет множество других методов, которые унаследованы QPushButton, мы воспользуемся ими позже для объяснения механизма сигнал/слот. В любом случае, метод `show()` не присутствует в списке, следовательно, это должен быть метод, передаваемый по наследству. Класс, от которого унаследован `QButton`, `QWidget`. Перейдя опять по ссылке, вы увидите целую связку методов, которые реализованы в классе `QWidget`, включая метод `show()`. Теперь мы можем понять, что было сделано в примере с кнопкой:

1. создаем экземпляр `QPushButton`, используя второй конструктор для установки текста на кнопке,
2. изменяем размер элемента в соответствии с размером его содержимого (текста),
3. делаем элемент главным элементом экземпляра `QApplication` `a`,
4. говорим элементу показать себя на экране, вызывая его метод `show()`, унаследованный от `QWidget`.

После вызова метода `exec()` приложение становится видимым для пользователя, отображая окно с кнопкой "Hello world!". Таким образом, GUI программа кое в чем отличается от процедурного приложения. Основное здесь в том, что приложение входит в так называемый "главный цикл событий" ("main event loop"). Это означает, что Qt приложение должно войти в главный цикл событий, чтобы начать обработку событий, потом программа ждет от пользователя действия и затем реагирует на него. Следующий раздел коротко описывает, что это значит для программиста и какие средства представляет Qt для обработки пользовательских событий.

(Для уже опытных пользователей: Кнопка не имеет родителя, он не указан в конструкторе, поэтому она - единственный элемент верхнего уровня и работает в локальном цикле событий, и не нуждается во входе в глобальный цикл событий, см. документацию по классу `QWidget` и "The KDE Library Reference Guide")

Подведение итогов:

Приложения Qt всегда создают экземпляр класса `QApplication`. Это обеспечивает нам возможность создавать окна, которые графически представляют программу пользователю и обеспечивают взаимодействие с ним. Содержимое окна называется Главный элемент ("Main Widget"). Это означает что все графические элементы базируются на классе `QWidget`, и могут быть любыми элементами, удовлетворяющими нужды приложения в части связи с пользователем. Поэтому все пользовательские элементы, которые необходимо сделать видимыми, должны быть унаследованы от `QWidget`.

#### Взаимодействие с пользователем

После прочтения последнего раздела вы должны уже знать:

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

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

Оконная система, таким образом, передает все сгенерированные пользователем события соответствующему приложению. QApplication затем передает их активному окну как `QEvent` и элементы сами решают, что с ними делать. Элемент получает событие и выполняет `QWidget::event(QEvent*)`, который решает, какое событие произошло и как реагировать; `event()`, следовательно, является главным обработчиком событий. Затем функция `event()` проводит событие через так называемый фильтр событий, который определяет, что случилось и что делать с событием. Если ни один фильтр не распознал событие, вызывается специальный обработчик событий. Таким образом, мы должны различать:

a) События клавиатуры -- клавиши TAB и Shift-TAB :

изменяет фокус ввода с клавиатуры с текущего элемента на следующий элемент в порядке изменения фокусов. Фокус может быть установлен для элемента вызовом `setFocusPolicy ()`. Изменение фокуса обрабатывается следующими обработчиками событий: :

- `virtual void focusInEvent ( QFocusEvent * )`
- `virtual void focusOutEvent ( QFocusEvent * )`

b) другой ввод с клавиатуры:

- `virtual void keyPressEvent ( QKeyEvent * )`
- `virtual void keyReleaseEvent ( QKeyEvent * )`

c) перемещение мыши:

- `virtual void mouseMoveEvent ( QMouseEvent * )`
- `virtual void enterEvent ( QEvent * )`
- `virtual void leaveEvent ( QEvent * )`

d) нажатие кнопок мыши:

- `virtual void mousePressEvent ( QMouseEvent * )`
- `virtual void mouseReleaseEvent ( QMouseEvent * )`
- `virtual void mouseDoubleClickEvent ( QMouseEvent * )`

e) события окна, содержащего элемент:

- `virtual void moveEvent ( QMoveEvent * )`
- `virtual void resizeEvent ( QResizeEvent * )`
- `virtual void closeEvent ( QCloseEvent * )`

Заметьте, что все функции событий virtual и protected; таким образом, вы можете переопределить события, которые вам нужны в ваших элементах, и указать, как ваш элемент должен на них реагировать. `QWidget` также содержит несколько других виртуальных методов, которые могут быть полезны в ваших программах; это, в общем-то, все, что вам сейчас необходимо знать о `QWidget`.

#### Взаимодействие объектов с помощью сигналов и слотов

Сейчас мы подошли к наиболее яркому преимуществу Qt инструментария: механизм сигнал/слот. Этот механизм предлагает очень удобное и полезное решение для реализации взаимодействия объектов, которое обычно выполняется с помощью `callback` функций для различных X-Window наборов элементов. Реализация с помощью `callback` функций требует очень строгого программирования и иногда превращает задачу построения пользовательского интерфейса в очень сложную (как об этом говорится в документации Qt и как это объяснено в книге K.Dalheimer Programming with Qt). Troll Tech изобрела новую систему, в которой объекты могут испускать (emit) сигналы, которые могут быть соединены (connected) с методами, объявленными как слоты (slot). Для программиста на C++ необходимо знать несколько простых вещей об этом механизме:

1. объявление класса, который использует сигналы/слоты, должно содержать макрос `Q_OBJECT` в начале (без ";"); и этот класс должен быть унаследован от `QObject`.
2. сигнал может испускаться по ключевому слову `emit`, например, `emit signal(parameters);` из любой функции-члена класса, который поддерживает механизм сигнал/слот,
3. все сигналы, используемые классом и не унаследованные от предков, должны быть добавлены в объявление класса в секцию `signals:`,
4. все методы, которые могут быть соединены с сигналами, должны быть объявлены в секциях с дополнительным ключевым словом `slot`, например, `public slots:` внутри объявления класса,
5. meta-object компилятор `moc` должен быть запущен для обработки файла заголовка для подстановки макросов и выдачи файла с реализацией (в которой не требуется разбираться). Выходной файл `moc` компилируется обычным компилятором C++.

Другой способ использовать сигналы без наследования от `QObject` - использовать класс `QSignal` - см. справочное руководство для получения дополнительной информации и примеров использования. В дальнейшем материале мы предполагаем, что используется наследование от `QObject`.

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

Теперь, для того, чтобы соединить сигнал и слот, вы должны использовать метод `connect()`, который предоставляет `QObject`, или, где это возможно, специальные методы, которые предоставляют объекты для установки соединения с определенными сигналами.

#### Пример использования

Чтобы объяснить, как организовать межобъектное взаимодействие, вернемся к нашему первому примеру и добавим в него простое соединение:

> `

>     #include <qapplication.h>
>     #include <qpushbutton.h>

>     int main( int argc, char **argv )
>     {
>     QApplication a( argc, argv );

>     QPushButton hello( "Hello world!" );
>     hello.resize( 100, 30 );

>     a.setMainWidget( &hello );

>     connect(&hello, SIGNAL( clicked() ), &a, SLOT( quit() ));

>     hello.show();
>     return a.exec();
>     }

> `

Как видно, единственное изменение, оживляющее кнопку, - использование метода `connect()`: `connect(&hello, SIGNAL( clicked() ), &a, SLOT( quit() ));`. Вот и все, что мы добавили. Что это значит? В классе `QObject` метод `connect()` объявляется так:

`bool connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * member )`

Это значит, что вы должны предоставить экземпляру QObject указатель на источник сигнала, имея в виду, что он должен уметь испускать этот сигнал, первым параметром; затем вы должны указать сигнал, для которого устанавливается соединение. Следующие два параметра - объект-получатель, который предоставляет слот, и фукнкция-член, которая и есть слот, выполняемый при получении сигнала.

Используя сигналы и слоты, объекты вашей программы могут взаимодействовать между собой проще, без точного определения типа объекта-получателя сигнала. Вы изучите подробнее использование этого механизма позднее в этой книге. Более подробная информация о механизме Сигнал/Слот также может быть найдена в "The KDE Library Reference Guide" и в Qt online-документации.

### 2.2 Что предлагает KDE

#### Библиотеки KDE 1.1.x

На момент написания книги KDevelop использует KDE 1.1, поэтому мы здесь рассмотрим библиотеки KDE этого релиза. Основные библиотеки KDE, которые вы будете использовать при разработке приложений KDE, следующие:

- библиотека KDE-Core содержит все классы, не являющиеся видимыми элементами, и обеспечивает реализацию тех возможностей, которые ваше приложение может использовать;
- библиотека KDE-UI содержит элементы пользовательского интерфейса, такие как панель меню, панели инструментов и т.п.;
- библиотека KFile содержит диалоговое окно выбора файлов.

Кроме того, для специальных целей KDE предоставляет следующие библиотеки:

- библиотека KHTMLW содержит элемент, который полностью интерпретирует HTML-файлы и используется в таких программах, как KDEHelp, KFM, KDevelop;
- библиотека KFM позволяет использовать KDE менеджер файлов из вашего приложения;
- библиотека KAb, KAddressBook. Реализует доступ к адресной книге, например, для приложений, работающих с email;
- библиотека KSpell предоставляет элементы для интеграции Ispell, программы проверки правописания, в приложения типа редакторов; используется в KEdit.

Теперь давайте посмотрим, что необходимо сделать, чтобы превратить наше Qt приложение в KDE приложение.

#### Пример приложения KDE

Сейчас вы увидите, что создание KDE приложения не на много сложнее, чем приложения для Qt. Для использования возможностей KDE вы просто должны включить в программу другие классы, и все будет сделано. Например, рассмотрим измененную версию примера программы Qt, приведенного выше:

> `

>     #include <kapp.h>
>     #include <qpushbutton.h>

>     int main( int argc, char **argv )
>     {
>     KApplication a( argc, argv );

>     QPushButton hello( "Hello world!" );
>     hello.resize( 100, 30 );

>     a.setTopWidget( &hello );

>     connect(&hello, SIGNAL( clicked() ), &a, SLOT( quit() ));

>     hello.show();
>     return a.exec();
>     }

> `

Как видно, в первую очередь мы изменили `QApplication` на `KApplication`. Далее, мы использовали вместо метода `setMainWidget()` метод `setTopWidget`, который вызывает `KApplication` для установки главного элемента (main widget). Вот и все! Ваше первое KDE приложение готово - вы только должны сообщить компилятору путь к файлам KDE, упомянутым в командах "#include", и линкеру - линковать с ключом -lkdecore.

Теперь, когда вы знаете, что в общем делает функция main(), как приложения становятся видимыми и как организовать взаимодействие пользователя и объектов программы, мы переходим к следующей главе, где мы сделаем наше первое приложение с помощью KDevelop - там вы сможете также протестировать все вышеупомянутое и посмотреть результат.

Сейчас вам будет полезно посмотреть дополнительно справочное руководство по Qt, особенно в части `QApplication`, `QWidget` и `QObject`, документацию по библиотеке KDE-Core для класса `KApplication`. Справочник "KDE Library Reference" содержит полное описание вызова конструкторов `QApplication` и `KApplication`, включая обработку аргументов командной строки.

Next Previous Table of Contents