В конце мы используем макрос 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"));