Выбрать главу

В конце мы используем макрос Q_DECLARE_INTERFACE() для назначения некоторого идентификатора интерфейсу. Этот идентификатор обычно имеет четыре компонента: инвертированное имя домена, определяющее создателя интерфейса, имя приложения, имя интерфейса и номер версии. При любом изменении интерфейса (например, при добавлении новой виртуальной функции или при изменении сигнатуры существующей функции) мы должны не забыть увеличить номер версии; в противном случае приложение может завершиться аварийно при попытке получения доступа к старой версии подключаемого модуля.

Это приложение реализуется в виде класса TextArtDialog. Мы будем показывать только тот программный код, который связан с применением подключаемых модулей. Давайте начнем с конструктора:

01 TextArtDialog::TextArtDialog(const QString &text, QWidget *parent)

02 : QDialog(parent)

03 {

04 listWidget = new QListWidget;

05 listWidget->setViewMode(QListWidget::IconMode);

06 listWidget->setMovement(QListWidget::Static);

07 listWidget->setIconSize(QSize(260, 80));

08 …

09 loadPlugins();

10 populateListWidget(text);

11 …

12 }

Конструктор создает виджет QListWidget, содержащий список доступных эффектов. Он вызывает закрытую функцию loadPlugins() для поиска и загрузки всех подключаемых модулей, реализующих интерфейс TextArtInterface, и заполняет список виджетов с помощью вызова другой закрытой функции — populateListWidget().

01 void TextArtDialog::loadPlugins()

02 {

03 QDir pluginDir(QApplication::applicationDirPath());

04 #if defined(Q_OS_WIN)

05 if (pluginDir.dirName().toLower() == "debug"

06 || pluginDir.dirName().toLower() == "release")

07 pluginDir.cdUp();

08 #elif defined(Q_OS_MAC)

09 if (pluginDir.dirName() == "MacOS") {

10 pluginDir.cdUp();

11 pluginDir.cdUp();

12 pluginDir.cdUp();

13 }

14 #endif

15 if (!pluginDir.cd("plugins"))

16 return;

17 foreach (QString fileName, pluginDir.entryList(QDir::Files)) {

18 QPluginLoader loader(pluginDir.absoluteFilePath(fileName));

19 if (TextArtInterface *interface =

20 qobject_cast<TextArtInterface *>(loader.instance()))

21 interfaces.append(interface);

22 }

23 }

В функции loadPlugins() мы пытаемся загрузить все файлы, находящиеся в каталоге приложения plugins. (В Windows исполняемый модуль приложения обычно находится в подкаталоге debug или release, поэтому поднимаемся на один каталог выше. В Mac OS X учитываем структуру группового каталога (bundle directory).)

Если файл, который мы пытаемся загрузить, является подключаемым модулем Qt и имеет ту же саму версию Qt, какую имеет приложение, функция QPluginLoader::instance() возвратит указатель QObject *, ссылающийся на подключаемый модуль Qt. Используем qobject_cast<T>() для проверки реализации в подключаемом модуле интерфейса TextArtInterface. При всяком успешном приведении типа мы добавляем интерфейс к списку интерфейсов приложения TextArtDialog (который имеет тип QList<TextArtInterface *>).

Для некоторых приложений может потребоваться загрузка двух или более различных интерфейсов, и в этом случае программный код по получении этих интерфейсов мог бы выглядеть следующим образом:

01 QObject *plugin = loader.instance();

02 if (TextArtInterface *i = qobject_cast<TextArtInterface *>(plugin))

03 textArtInterfaces.append(i);

04 if (BorderArtInterface *i = qobject_cast<BorderArtInterface *>(plugin))

05 borderArtInterfaces.append(i);

06 if (TextureInterface *i = qobject_cast<TextureInterface *>(plugin))

07 textureInterfaces.append(i);

Тип одного подключаемого модуля может успешно приводиться к нескольким указателям интерфейсов, поскольку подключаемые модули могут обеспечивать несколько интерфейсов, используя множественное наследование.

01 void TextArtDialog::populateListWidget(const QString &text)

02 {

03 QSize iconSize = listWidget->iconSize();

04 QPen pen(QColor("darkseagreen"));

05 QLinearGradient gradient(0, 0, iconSize.width() / 2,

06 iconSize.height() / 2);

07 gradient.setColorAt(0.0. QColor("darkolivegreen"));