QT 4: программирование GUI на С++
Шрифт:
Этим завершается реализация класса CursorHandler — подключаемого модуля, предназначенного для работы с изображениями курсоров. Подключаемые модули для изображений других форматов могли бы создаваться аналогично, хотя в некоторых случаях может потребоваться реализация дополнительных функций программного интерфейса QImageIOHandler, в частности функций, используемых для записи изображений. Подключаемые модули другого вида, например кодировки текста или драйверы баз данных, создаются по тому же самому образцу: реализуются класс—оболочка, обеспечивающий общий программный интерфейс подключаемых модулей, который может использоваться приложением, и обработчик, обеспечивающий базовую функциональность.
Файл .pro
По умолчанию файлы .pro используют шаблон app, но здесь мы должны указать шаблон lib, потому что подключаемый модуль является библиотекой, а не автономным приложением. Строка с элементом CONFIG указывает Qt на то, что у нас не простая библиотека, а библиотека подключаемого модуля. Элемент DESTDIR определяет каталог размещения подключаемого модуля. Каждый подключаемый модуль Qt должен находиться в соответствующем подкаталоге каталога plugins, и поскольку наш подключаемый модуль обеспечивает новый формат изображений, помещаем его в plugins/imageformats. Список имен каталогов и типов подключаемых модулей приводится на веб-странице В данном случае мы предполагаем, что переменная среды QTDIR определяет каталог, в котором находится Qt.
Для Qt в рабочем (release) и отладочном (debug) режимах создаются различные подключаемые модули, поэтому, если установлены обе версии Qt, имеет смысл указать в файле .pro ту из них, которая будет использоваться, добавляя строку
Приложения, использующие подключаемые модули Qt, должны разворачиваться совместно со своими подключаемыми модулями. Подключаемые модули Qt должны располагаться в конкретных подкаталогах (например, в imageformats для форматов изображений). Приложения Qt ищут подключаемые модули в каталоге plugins, который располагается в каталоге размещения исполняемого модуля приложения, поэтому поиск подключаемых модулей изображений будет выполняться в application_dir/plugins/imageformats. Если требуется развернуть подключаемые модули Qt в другом каталоге, можно установить дополнительный путь поиска, используя функцию QCoreApplication::addLibraryPath.
Как обеспечить в приложении возможность подключения модулей
Подключаемый к приложению модуль является динамической библиотекой, которая реализует какой-нибудь один или несколько интерфейсов. Интерфейс — это класс, содержащий только чисто виртуальные функции. Связь между приложением и подключаемыми модулями осуществляется через виртуальную таблицу интерфейса. В этом разделе мы основное внимание уделим способам взаимодействия приложения Qt с подключаемым модулем через его интерфейсы, а в следующем разделе покажем, как можно реализовать подключаемый модуль.
Чтобы продемонстрировать конкретный пример, создадим простое приложение Text Art (искусство отображения текста), показанное на рис. 19.3. Специальные эффекты отображения текста обеспечиваются подключаемыми модулями; приложение получает список текстовых эффектов, создаваемых каждым подключаемым модулем, и проходит в цикле по этому списку, показывая
Рис. 19.3. Приложение Text Art.
В приложении Text Art определяется один интерфейс:
В классе интерфейса обычно объявляются виртуальный деструктор, виртуальная функция, возвращающая список QStringList, и одна или несколько других виртуальных функций. Деструктор объявляется прежде всего для того, чтобы компилятор не жаловался на отсутствие виртуального деструктора в классе, который имеет виртуальные функции. В данном примере функция effects возвращает список текстовых эффектов, которые могут создаваться подключаемым модулем. Этот список можно рассматривать как список ключей. При каждом вызове одной из функций мы передаем эти ключи в качестве первого аргумента, позволяя реализовать в одном подключаемом модуле несколько эффектов.
В конце мы используем макрос Q_DECLARE_INTERFACE для назначения некоторого идентификатора интерфейсу. Этот идентификатор обычно имеет четыре компонента: инвертированное имя домена, определяющее создателя интерфейса, имя приложения, имя интерфейса и номер версии. При любом изменении интерфейса (например, при добавлении новой виртуальной функции или при изменении сигнатуры существующей функции) мы должны не забыть увеличить номер версии; в противном случае приложение может завершиться аварийно при попытке получения доступа к старой версии подключаемого модуля.
Это приложение реализуется в виде класса TextArtDialog. Мы будем показывать только тот программный код, который связан с применением подключаемых модулей. Давайте начнем с конструктора:
Конструктор создает виджет QListWidget, содержащий список доступных эффектов. Он вызывает закрытую функцию loadPlugins для поиска и загрузки всех подключаемых модулей, реализующих интерфейс TextArtInterface, и заполняет список виджетов с помощью вызова другой закрытой функции — populateListWidget.